Initial Contribution
diff --git a/dx/.classpath b/dx/.classpath
new file mode 100644
index 0000000..5b6d9c7
--- /dev/null
+++ b/dx/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dx/.project b/dx/.project
new file mode 100644
index 0000000..bcae232
--- /dev/null
+++ b/dx/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dx</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/dx/Android.mk b/dx/Android.mk
new file mode 100644
index 0000000..7ae0039
--- /dev/null
+++ b/dx/Android.mk
@@ -0,0 +1,65 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# the dx script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dx
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dx$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dx | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the jasmin script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := jasmin
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/jasmin.jar
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the jasmin lib
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE := jasmin.jar
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin.jar | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-target)
+	$(hide) chmod 644 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+		src \
+	))
+
+include $(subdirs)
diff --git a/dx/README.txt b/dx/README.txt
new file mode 100644
index 0000000..5421e7b
--- /dev/null
+++ b/dx/README.txt
@@ -0,0 +1,2 @@
+Home of Dalvik eXchange, the thing that takes in class files and
+reformulates them for consumption in the VM.
diff --git a/dx/etc/bytecode.txt b/dx/etc/bytecode.txt
new file mode 100644
index 0000000..f1df5bf
--- /dev/null
+++ b/dx/etc/bytecode.txt
@@ -0,0 +1,278 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Bytecode definition file
+#
+# Columns are:
+#   hex for opcode
+#   format
+#   has result register (y/n)
+#   opcode name
+
+00 10x n nop
+01 12x y move
+02 22x y move/from16
+03 32x y move/16
+04 12x y move-wide
+05 22x y move-wide/from16
+06 32x y move-wide/16
+07 12x y move-object
+08 22x y move-object/from16
+09 32x y move-object/16
+0a 11x y move-result
+0b 11x y move-result-wide
+0c 11x y move-result-object
+0d 11x y move-exception
+0e 10x n return-void
+0f 11x n return
+10 11x n return-wide
+11 11x n return-object
+12 11n y const/4
+13 21s y const/16
+14 31i y const
+15 21h y const/high16
+16 21s y const-wide/16
+17 31i y const-wide/32
+18 51l y const-wide
+19 21h y const-wide/high16
+1a 21c y const-string
+1b 31c y const-string/jumbo
+1c 21c y const-class
+1d 11x n monitor-enter
+1e 11x n monitor-exit
+1f 21c y check-cast
+20 22c y instance-of
+21 12x y array-length
+22 21c y new-instance
+23 22c y new-array
+24 35c n filled-new-array
+25 3rc n filled-new-array/range
+26 31t n fill-array-data
+27 11x n throw
+28 10t n goto
+29 20t n goto/16
+2a 30t n goto/32
+2b 31t n packed-switch
+2c 31t n sparse-switch
+2d 23x y cmpl-float
+2e 23x y cmpg-float
+2f 23x y cmpl-double
+30 23x y cmpg-double
+31 23x y cmp-long
+32 22t n if-eq
+33 22t n if-ne
+34 22t n if-lt
+35 22t n if-ge
+36 22t n if-gt
+37 22t n if-le
+38 21t n if-eqz
+39 21t n if-nez
+3a 21t n if-ltz
+3b 21t n if-gez
+3c 21t n if-gtz
+3d 21t n if-lez
+3e 10x n unused-3e
+3f 10x n unused-3f
+40 10x n unused-40
+41 10x n unused-41
+42 10x n unused-42
+43 10x n unused-43
+44 23x y aget
+45 23x y aget-wide
+46 23x y aget-object
+47 23x y aget-boolean
+48 23x y aget-byte
+49 23x y aget-char
+4a 23x y aget-short
+4b 23x n aput
+4c 23x n aput-wide
+4d 23x n aput-object
+4e 23x n aput-boolean
+4f 23x n aput-byte
+50 23x n aput-char
+51 23x n aput-short
+52 22c y iget
+53 22c y iget-wide
+54 22c y iget-object
+55 22c y iget-boolean
+56 22c y iget-byte
+57 22c y iget-char
+58 22c y iget-short
+59 22c n iput
+5a 22c n iput-wide
+5b 22c n iput-object
+5c 22c n iput-boolean
+5d 22c n iput-byte
+5e 22c n iput-char
+5f 22c n iput-short
+60 21c y sget
+61 21c y sget-wide
+62 21c y sget-object
+63 21c y sget-boolean
+64 21c y sget-byte
+65 21c y sget-char
+66 21c y sget-short
+67 21c n sput
+68 21c n sput-wide
+69 21c n sput-object
+6a 21c n sput-boolean
+6b 21c n sput-byte
+6c 21c n sput-char
+6d 21c n sput-short
+6e 35c n invoke-virtual
+6f 35c n invoke-super
+70 35c n invoke-direct
+71 35c n invoke-static
+72 35c n invoke-interface
+73 10x n unused-73
+74 3rc n invoke-virtual/range
+75 3rc n invoke-super/range
+76 3rc n invoke-direct/range
+77 3rc n invoke-static/range
+78 3rc n invoke-interface/range
+79 10x n unused-79
+7a 10x n unused-7a
+7b 12x y neg-int
+7c 12x y not-int
+7d 12x y neg-long
+7e 12x y not-long
+7f 12x y neg-float
+80 12x y neg-double
+81 12x y int-to-long
+82 12x y int-to-float
+83 12x y int-to-double
+84 12x y long-to-int
+85 12x y long-to-float
+86 12x y long-to-double
+87 12x y float-to-int
+88 12x y float-to-long
+89 12x y float-to-double
+8a 12x y double-to-int
+8b 12x y double-to-long
+8c 12x y double-to-float
+8d 12x y int-to-byte
+8e 12x y int-to-char
+8f 12x y int-to-short
+90 23x y add-int
+91 23x y sub-int
+92 23x y mul-int
+93 23x y div-int
+94 23x y rem-int
+95 23x y and-int
+96 23x y or-int
+97 23x y xor-int
+98 23x y shl-int
+99 23x y shr-int
+9a 23x y ushr-int
+9b 23x y add-long
+9c 23x y sub-long
+9d 23x y mul-long
+9e 23x y div-long
+9f 23x y rem-long
+a0 23x y and-long
+a1 23x y or-long
+a2 23x y xor-long
+a3 23x y shl-long
+a4 23x y shr-long
+a5 23x y ushr-long
+a6 23x y add-float
+a7 23x y sub-float
+a8 23x y mul-float
+a9 23x y div-float
+aa 23x y rem-float
+ab 23x y add-double
+ac 23x y sub-double
+ad 23x y mul-double
+ae 23x y div-double
+af 23x y rem-double
+b0 12x y add-int/2addr
+b1 12x y sub-int/2addr
+b2 12x y mul-int/2addr
+b3 12x y div-int/2addr
+b4 12x y rem-int/2addr
+b5 12x y and-int/2addr
+b6 12x y or-int/2addr
+b7 12x y xor-int/2addr
+b8 12x y shl-int/2addr
+b9 12x y shr-int/2addr
+ba 12x y ushr-int/2addr
+bb 12x y add-long/2addr
+bc 12x y sub-long/2addr
+bd 12x y mul-long/2addr
+be 12x y div-long/2addr
+bf 12x y rem-long/2addr
+c0 12x y and-long/2addr
+c1 12x y or-long/2addr
+c2 12x y xor-long/2addr
+c3 12x y shl-long/2addr
+c4 12x y shr-long/2addr
+c5 12x y ushr-long/2addr
+c6 12x y add-float/2addr
+c7 12x y sub-float/2addr
+c8 12x y mul-float/2addr
+c9 12x y div-float/2addr
+ca 12x y rem-float/2addr
+cb 12x y add-double/2addr
+cc 12x y sub-double/2addr
+cd 12x y mul-double/2addr
+ce 12x y div-double/2addr
+cf 12x y rem-double/2addr
+d0 22s y add-int/lit16
+d1 22s y rsub-int
+d2 22s y mul-int/lit16
+d3 22s y div-int/lit16
+d4 22s y rem-int/lit16
+d5 22s y and-int/lit16
+d6 22s y or-int/lit16
+d7 22s y xor-int/lit16
+d8 22b y add-int/lit8
+d9 22b y rsub-int/lit8
+da 22b y mul-int/lit8
+db 22b y div-int/lit8
+dc 22b y rem-int/lit8
+dd 22b y and-int/lit8
+de 22b y or-int/lit8
+df 22b y xor-int/lit8
+e0 22b y shl-int/lit8
+e1 22b y shr-int/lit8
+e2 22b y ushr-int/lit8
+e3 10x n unused-e3
+e4 10x n unused-e4
+e5 10x n unused-e5
+e6 10x n unused-e6
+e7 10x n unused-e7
+e8 10x n unused-e8
+e9 10x n unused-e9
+ea 10x n unused-ea
+eb 10x n unused-eb
+ec 10x n unused-ec
+ed 10x n unused-ed
+ee 10x n unused-ee
+ef 10x n unused-ef
+f0 10x n unused-f0
+f1 10x n unused-f1
+f2 10x n unused-f2
+f3 10x n unused-f3
+f4 10x n unused-f4
+f5 10x n unused-f5
+f6 10x n unused-f6
+f7 10x n unused-f7
+f8 10x n unused-f8
+f9 10x n unused-f9
+fa 10x n unused-fa
+fb 10x n unused-fb
+fc 10x n unused-fc
+fd 10x n unused-fd
+fe 10x n unused-fe
+ff 10x n unused-ff
diff --git a/dx/etc/dx b/dx/etc/dx
new file mode 100644
index 0000000..dae5874
--- /dev/null
+++ b/dx/etc/dx
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dx.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+    libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+    libdir=`dirname "$progdir"`/framework
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+    echo `basename "$prog"`": can't find $jarfile"
+    exit 1
+fi
+
+javaOpts=""
+
+# If you want DX to have more memory when executing, uncomment the following
+# line and adjust the value accordingly. Use "java -X" for a list of options
+# you can pass here.
+# 
+# javaOpts="-Xmx256M"
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dx). This makes it possible for you to
+# add a command-line parameter such as "-JXmx256M" in your ant scripts, for
+# example.
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+	jarpath=`cygpath -w  "$libdir/$jarfile"`
+else
+	jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/dx/etc/dx.bat b/dx/etc/dx.bat
new file mode 100755
index 0000000..2686d6a
--- /dev/null
+++ b/dx/etc/dx.bat
@@ -0,0 +1,51 @@
+@echo off
+REM Copyright (C) 2007 The Android Open Source Project
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+REM don't modify the caller's environment
+setlocal
+
+REM Locate dx.jar in the directory where dx.bat was found and start it.
+
+REM Set up prog to be the path of this script, including following symlinks,
+REM and set up progdir to be the fully-qualified pathname of its directory.
+set prog=%~f0
+
+REM Change current directory to where dx is, to avoid issues with directories
+REM containing whitespaces.
+cd /d %~dp0
+
+set jarfile=dx.jar
+set frameworkdir=
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+    set frameworkdir=lib\
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+    set frameworkdir=..\framework\
+
+:JarFileOk
+
+set jarpath=%frameworkdir%%jarfile%
+
+set javaOpts=
+
+REM If you want DX to have more memory when executing, uncomment the
+REM following line and adjust the value accordingly. Use "java -X" for
+REM a list of options you can pass here.
+REM 
+REM set javaOpts=-Xmx256M
+
+call java %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %*
+
diff --git a/dx/etc/jasmin b/dx/etc/jasmin
new file mode 100644
index 0000000..f44c16f
--- /dev/null
+++ b/dx/etc/jasmin
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+exec java -jar $libdir/jasmin.jar "$@"
diff --git a/dx/etc/jasmin.jar b/dx/etc/jasmin.jar
new file mode 100644
index 0000000..87db0d0
--- /dev/null
+++ b/dx/etc/jasmin.jar
Binary files differ
diff --git a/dx/etc/manifest.txt b/dx/etc/manifest.txt
new file mode 100644
index 0000000..46bbe63
--- /dev/null
+++ b/dx/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dx.command.Main
diff --git a/dx/etc/opcode-gen b/dx/etc/opcode-gen
new file mode 100755
index 0000000..390a6c3
--- /dev/null
+++ b/dx/etc/opcode-gen
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# opcode-gen <file>
+#
+# Use the file bytecodes.txt to generate code inside <file>, based on
+# the directives found in that file:
+#
+#     opcodes:   static final ints for each opcode
+#     dops:      static final objects for each opcode
+#     dops-init: initialization code for the "dops"
+
+file="$1"
+tmpfile="/tmp/$$.txt"
+
+if [ "x$1" = "x" ]; then
+    echo "must specify a file"
+    exit 1
+fi
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+bytecodeFile="$progdir/bytecode.txt"
+
+awk -v "bytecodeFile=$bytecodeFile" '
+
+BEGIN {
+    readBytecodes();
+    consumeUntil = "";
+}
+
+consumeUntil != "" {
+    if (index($0, consumeUntil) != 0) {
+        consumeUntil = "";
+    } else {
+        next;
+    }
+}
+
+/BEGIN\(opcodes\)/ {
+    consumeUntil = "END(opcodes)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        printf("    public static final int %s = 0x%s;\n",
+               uppername[i], hex[i]);
+    }
+
+    next;
+}
+
+/BEGIN\(dops\)/ {
+    consumeUntil = "END(dops)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        if (index(name[i], "unused") != 0) {
+            continue;
+        }
+        printf("    public static final Dop %s =\n" \
+               "        new Dop(DalvOps.%s, DalvOps.%s,\n" \
+               "            Form%s.THE_ONE, %s, \"%s\");\n\n",
+               uppername[i], uppername[i], family[i], format[i], hasres[i],
+               name[i]);
+    }
+
+    next;
+}
+
+/BEGIN\(dops-init\)/ {
+    consumeUntil = "END(dops-init)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        if (index(name[i], "unused") != 0) {
+            continue;
+        }
+        printf("        set(%s);\n", uppername[i]);
+    }
+
+    next;
+}
+
+{ print; }
+
+function readBytecodes(i, parts) {
+    for (i = 0; i < 256; i++) {
+        $0 = "";
+        while (($0 == "") || (index($0, "#") != 0)) {
+            if ((getline <bytecodeFile) != 1) {
+                print "trouble reading bytecode file";
+                exit 1;
+            }
+        }
+        split($0, parts);
+        hex[i] = parts[1];
+        format[i] = parts[2];
+        hasres[i] = (parts[3] == "n") ? "false" : "true";
+        name[i] = parts[4];
+        uppername[i] = toupper(parts[4]);
+        gsub("[---/]", "_", uppername[i]);
+        split(name[i], parts, "/");
+        family[i] = toupper(parts[1]);
+        gsub("-", "_", family[i]);
+    }
+}
+' "$file" > "$tmpfile"
+
+cp "$tmpfile" "$file"
+rm "$tmpfile"
diff --git a/dx/etc/run-opcode-gen b/dx/etc/run-opcode-gen
new file mode 100755
index 0000000..70e9707
--- /dev/null
+++ b/dx/etc/run-opcode-gen
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run this from this directory.
+
+./opcode-gen ../src/com/android/dx/dex/code/DalvOps.java
+./opcode-gen ../src/com/android/dx/dex/code/Dops.java
+
+
diff --git a/dx/src/Android.mk b/dx/src/Android.mk
new file mode 100644
index 0000000..ef3d32d
--- /dev/null
+++ b/dx/src/Android.mk
@@ -0,0 +1,31 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+
+# dx java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+
+LOCAL_MODULE:= dx
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the documentation
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-subdir-html-files)
+
+LOCAL_MODULE:= dx
+LOCAL_DROIDDOC_OPTIONS := -hidden
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_IS_HOST_MODULE := true
+
+include $(BUILD_DROIDDOC)
+
diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java
new file mode 100644
index 0000000..c745d34
--- /dev/null
+++ b/dx/src/com/android/dx/Version.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx;
+
+/**
+ * Version number for dx.
+ */
+public class Version {
+    /** non-null; version string */
+    public static final String VERSION = "1.1";
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
new file mode 100644
index 0000000..12e1f74
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Attribute class for <code>AnnotationDefault</code> attributes.
+ */
+public final class AttAnnotationDefault extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "AnnotationDefault";
+
+    /** non-null; the annotation default value */
+    private final Constant value;
+
+    /** &gt;= 0; attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param value non-null; the annotation default value
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttAnnotationDefault(Constant value, int byteLength) {
+        super(ATTRIBUTE_NAME);
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        this.value = value;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the annotation default value.
+     * 
+     * @return non-null; the value
+     */
+    public Constant getValue() {
+        return value;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttCode.java b/dx/src/com/android/dx/cf/attrib/AttCode.java
new file mode 100644
index 0000000..f00da2f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttCode.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard <code>Code</code> attributes.
+ */
+public final class AttCode extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Code";
+
+    /** &gt;= 0; the stack size */
+    private final int maxStack;
+
+    /** &gt;= 0; the number of locals */
+    private final int maxLocals;
+
+    /** non-null; array containing the bytecode per se */
+    private final BytecodeArray code;
+
+    /** non-null; the exception table */
+    private final ByteCatchList catches;
+
+    /** non-null; the associated list of attributes */
+    private final AttributeList attributes;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param maxStack &gt;= 0; the stack size
+     * @param maxLocals &gt;= 0; the number of locals
+     * @param code non-null; array containing the bytecode per se
+     * @param catches non-null; the exception table
+     * @param attributes non-null; the associated list of attributes
+     */
+    public AttCode(int maxStack, int maxLocals, BytecodeArray code,
+                   ByteCatchList catches, AttributeList attributes) {
+        super(ATTRIBUTE_NAME);
+
+        if (maxStack < 0) {
+            throw new IllegalArgumentException("maxStack < 0");
+        }
+
+        if (maxLocals < 0) {
+            throw new IllegalArgumentException("maxLocals < 0");
+        }
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        try {
+            if (catches.isMutable()) {
+                throw new MutabilityException("catches.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("catches == null");
+        }
+
+        try {
+            if (attributes.isMutable()) {
+                throw new MutabilityException("attributes.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("attributes == null");
+        }
+
+        this.maxStack = maxStack;
+        this.maxLocals = maxLocals;
+        this.code = code;
+        this.catches = catches;
+        this.attributes = attributes;
+    }
+
+    public int byteLength() {
+        return 10 + code.byteLength() + catches.byteLength() +
+            attributes.byteLength();
+    }
+
+    /**
+     * Gets the maximum stack size.
+     * 
+     * @return &gt;= 0; the maximum stack size
+     */
+    public int getMaxStack() {
+        return maxStack;
+    }
+
+    /**
+     * Gets the number of locals.
+     * 
+     * @return &gt;= 0; the number of locals
+     */
+    public int getMaxLocals() {
+        return maxLocals;
+    }
+
+    /**
+     * Gets the bytecode array.
+     * 
+     * @return non-null; the bytecode array
+     */
+    public BytecodeArray getCode() {
+        return code;
+    }
+
+    /**
+     * Gets the exception table.
+     * 
+     * @return non-null; the exception table
+     */
+    public ByteCatchList getCatches() {
+        return catches;
+    }
+
+    /**
+     * Gets the associated attribute list.
+     * 
+     * @return non-null; the attribute list
+     */
+    public AttributeList getAttributes() {
+        return attributes;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttConstantValue.java b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
new file mode 100644
index 0000000..a84da43
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Attribute class for standard <code>ConstantValue</code> attributes.
+ */
+public final class AttConstantValue extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "ConstantValue";
+
+    /** non-null; the constant value */
+    private final TypedConstant constantValue;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param constantValue non-null; the constant value, which must
+     * be an instance of one of: <code>CstString</code>,
+     * <code>CstInteger</code>, <code>CstLong</code>,
+     * <code>CstFloat</code>, or <code>CstDouble</code>
+     */
+    public AttConstantValue(TypedConstant constantValue) {
+        super(ATTRIBUTE_NAME);
+
+        if (!((constantValue instanceof CstString) ||
+               (constantValue instanceof CstInteger) ||
+               (constantValue instanceof CstLong) ||
+               (constantValue instanceof CstFloat) ||
+               (constantValue instanceof CstDouble))) {
+            if (constantValue == null) {
+                throw new NullPointerException("constantValue == null");
+            }
+            throw new IllegalArgumentException("bad type for constantValue");
+        }
+
+        this.constantValue = constantValue;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the constant value of this instance. The returned value
+     * is an instance of one of: <code>CstString</code>,
+     * <code>CstInteger</code>, <code>CstLong</code>,
+     * <code>CstFloat</code>, or <code>CstDouble</code>.
+     * 
+     * @return non-null; the constant value
+     */
+    public TypedConstant getConstantValue() {
+        return constantValue;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttDeprecated.java b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
new file mode 100644
index 0000000..cd1dd24
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard <code>Deprecated</code> attributes.
+ */
+public final class AttDeprecated extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Deprecated";
+
+    /**
+     * Constructs an instance.
+     */
+    public AttDeprecated() {
+        super(ATTRIBUTE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 6;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
new file mode 100644
index 0000000..7cccad7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Attribute class for standards-track <code>EnclosingMethod</code>
+ * attributes.
+ */
+public final class AttEnclosingMethod extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "EnclosingMethod";
+
+    /** non-null; the innermost enclosing class */
+    private final CstType type;
+
+    /** null-ok; the name-and-type of the innermost enclosing method, if any */
+    private final CstNat method;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param type non-null; the innermost enclosing class
+     * @param method null-ok; the name-and-type of the innermost enclosing
+     * method, if any
+     */
+    public AttEnclosingMethod(CstType type, CstNat method) {
+        super(ATTRIBUTE_NAME);
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.type = type;
+        this.method = method;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 10;
+    }
+
+    /**
+     * Gets the innermost enclosing class.
+     * 
+     * @return non-null; the innermost enclosing class
+     */
+    public CstType getEnclosingClass() {
+        return type;
+    }
+
+    /**
+     * Gets the name-and-type of the innermost enclosing method, if
+     * any.
+     * 
+     * @return null-ok; the name-and-type of the innermost enclosing
+     * method, if any
+     */
+    public CstNat getMethod() {
+        return method;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttExceptions.java b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
new file mode 100644
index 0000000..59e624e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard <code>Exceptions</code> attributes.
+ */
+public final class AttExceptions extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Exceptions";
+
+    /** non-null; list of exception classes */
+    private final TypeList exceptions;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param exceptions non-null; list of classes, presumed but not
+     * verified to be subclasses of <code>Throwable</code>
+     */
+    public AttExceptions(TypeList exceptions) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (exceptions.isMutable()) {
+                throw new MutabilityException("exceptions.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("exceptions == null");
+        }
+
+        this.exceptions = exceptions;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + exceptions.size() * 2;
+    }
+
+    /**
+     * Gets the list of classes associated with this instance. In
+     * general, these classes are not pre-verified to be subclasses of
+     * <code>Throwable</code>.
+     * 
+     * @return non-null; the list of classes
+     */
+    public TypeList getExceptions() {
+        return exceptions;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
new file mode 100644
index 0000000..df30539
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard <code>InnerClasses</code> attributes.
+ */
+public final class AttInnerClasses extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "InnerClasses";
+
+    /** non-null; list of inner class entries */
+    private final InnerClassList innerClasses;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param innerClasses non-null; list of inner class entries
+     */
+    public AttInnerClasses(InnerClassList innerClasses) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (innerClasses.isMutable()) {
+                throw new MutabilityException("innerClasses.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("innerClasses == null");
+        }
+
+        this.innerClasses = innerClasses;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + innerClasses.size() * 8;
+    }
+
+    /**
+     * Gets the list of "inner class" entries associated with this instance.
+     * 
+     * @return non-null; the list
+     */
+    public InnerClassList getInnerClasses() {
+        return innerClasses;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
new file mode 100644
index 0000000..c5e65e8
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard <code>LineNumberTable</code> attributes.
+ */
+public final class AttLineNumberTable extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LineNumberTable";
+
+    /** non-null; list of line number entries */
+    private final LineNumberList lineNumbers;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param lineNumbers non-null; list of line number entries
+     */
+    public AttLineNumberTable(LineNumberList lineNumbers) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (lineNumbers.isMutable()) {
+                throw new MutabilityException("lineNumbers.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("lineNumbers == null");
+        }
+
+        this.lineNumbers = lineNumbers;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + 4 * lineNumbers.size();
+    }
+
+    /**
+     * Gets the list of "line number" entries associated with this instance.
+     * 
+     * @return non-null; the list
+     */
+    public LineNumberList getLineNumbers() {
+        return lineNumbers;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
new file mode 100644
index 0000000..893f254
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard <code>LocalVariableTable</code> attributes.
+ */
+public final class AttLocalVariableTable extends BaseLocalVariables {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LocalVariableTable";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param localVariables non-null; list of local variable entries
+     */
+    public AttLocalVariableTable(LocalVariableList localVariables) {
+        super(ATTRIBUTE_NAME, localVariables);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
new file mode 100644
index 0000000..7037b74
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard <code>LocalVariableTypeTable</code> attributes.
+ */
+public final class AttLocalVariableTypeTable extends BaseLocalVariables {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LocalVariableTypeTable";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param localVariables non-null; list of local variable entries
+     */
+    public AttLocalVariableTypeTable(LocalVariableList localVariables) {
+        super(ATTRIBUTE_NAME, localVariables);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
new file mode 100644
index 0000000..583ab17
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard <code>RuntimeInvisibleAnnotations</code>
+ * attributes.
+ */
+public final class AttRuntimeInvisibleAnnotations extends BaseAnnotations {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "RuntimeInvisibleAnnotations";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotations non-null; the list of annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeInvisibleAnnotations(Annotations annotations,
+            int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
new file mode 100644
index 0000000..08865e1
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard
+ * <code>RuntimeInvisibleParameterAnnotations</code> attributes.
+ */
+public final class AttRuntimeInvisibleParameterAnnotations
+        extends BaseParameterAnnotations {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME =
+        "RuntimeInvisibleParameterAnnotations";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param parameterAnnotations non-null; the parameter annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeInvisibleParameterAnnotations(
+            AnnotationsList parameterAnnotations, int byteLength) {
+        super(ATTRIBUTE_NAME, parameterAnnotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
new file mode 100644
index 0000000..c61acb5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard <code>RuntimeVisibleAnnotations</code>
+ * attributes.
+ */
+public final class AttRuntimeVisibleAnnotations extends BaseAnnotations {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "RuntimeVisibleAnnotations";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotations non-null; the list of annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeVisibleAnnotations(Annotations annotations,
+            int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
new file mode 100644
index 0000000..dfe57b2
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard <code>RuntimeVisibleParameterAnnotations</code>
+ * attributes.
+ */
+public final class AttRuntimeVisibleParameterAnnotations
+        extends BaseParameterAnnotations {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME =
+        "RuntimeVisibleParameterAnnotations";
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotations non-null; the parameter annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeVisibleParameterAnnotations(
+            AnnotationsList annotations, int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSignature.java b/dx/src/com/android/dx/cf/attrib/AttSignature.java
new file mode 100644
index 0000000..97edbbd
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSignature.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standards-track <code>Signature</code> attributes.
+ */
+public final class AttSignature extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Signature";
+
+    /** non-null; the signature string */
+    private final CstUtf8 signature;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param signature non-null; the signature string
+     */
+    public AttSignature(CstUtf8 signature) {
+        super(ATTRIBUTE_NAME);
+
+        if (signature == null) {
+            throw new NullPointerException("signature == null");
+        }
+
+        this.signature = signature;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the signature string.
+     * 
+     * @return non-null; the signature string
+     */
+    public CstUtf8 getSignature() {
+        return signature;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSourceFile.java b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
new file mode 100644
index 0000000..f087217
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standard <code>SourceFile</code> attributes.
+ */
+public final class AttSourceFile extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "SourceFile";
+
+    /** non-null; name of the source file */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param sourceFile non-null; the name of the source file
+     */
+    public AttSourceFile(CstUtf8 sourceFile) {
+        super(ATTRIBUTE_NAME);
+
+        if (sourceFile == null) {
+            throw new NullPointerException("sourceFile == null");
+        }
+
+        this.sourceFile = sourceFile;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the source file name of this instance.
+     * 
+     * @return non-null; the source file
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSynthetic.java b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
new file mode 100644
index 0000000..daa9b0c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard <code>Synthetic</code> attributes.
+ */
+public final class AttSynthetic extends BaseAttribute {
+    /** non-null; attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Synthetic";
+
+    /**
+     * Constructs an instance.
+     */
+    public AttSynthetic() {
+        super(ATTRIBUTE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 6;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
new file mode 100644
index 0000000..0163e2c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for annotations attributes.
+ */
+public abstract class BaseAnnotations extends BaseAttribute {
+    /** non-null; list of annotations */
+    private final Annotations annotations;
+
+    /** &gt;= 0; attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param attributeName non-null; the name of the attribute
+     * @param annotations non-null; the list of annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public BaseAnnotations(String attributeName, Annotations annotations,
+            int byteLength) {
+        super(attributeName);
+
+        try {
+            if (annotations.isMutable()) {
+                throw new MutabilityException("annotations.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.annotations = annotations;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the list of annotations associated with this instance.
+     * 
+     * @return non-null; the list
+     */
+    public final Annotations getAnnotations() {
+        return annotations;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAttribute.java b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
new file mode 100644
index 0000000..ef1c6ac
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.iface.Attribute;
+
+/**
+ * Base implementation of {@link Attribute}, which directly stores
+ * the attribute name but leaves the rest up to subclasses.
+ */
+public abstract class BaseAttribute implements Attribute {
+    /** non-null; attribute name */
+    private final String name;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param name non-null; attribute name
+     */
+    public BaseAttribute(String name) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        this.name = name;
+    }
+
+    /** {@inheritDoc} */
+    public String getName() {
+        return name;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
new file mode 100644
index 0000000..a39e724
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base attribute class for standard <code>LocalVariableTable</code>
+ * and <code>LocalVariableTypeTable</code> attributes.
+ */
+public abstract class BaseLocalVariables extends BaseAttribute {
+    /** non-null; list of local variable entries */
+    private final LocalVariableList localVariables;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param name non-null; attribute name
+     * @param localVariables non-null; list of local variable entries
+     */
+    public BaseLocalVariables(String name,
+            LocalVariableList localVariables) {
+        super(name);
+
+        try {
+            if (localVariables.isMutable()) {
+                throw new MutabilityException("localVariables.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("localVariables == null");
+        }
+
+        this.localVariables = localVariables;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        return 8 + localVariables.size() * 10;
+    }
+
+    /**
+     * Gets the list of "local variable" entries associated with this instance.
+     * 
+     * @return non-null; the list
+     */
+    public final LocalVariableList getLocalVariables() {
+        return localVariables;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
new file mode 100644
index 0000000..a927e3d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for parameter annotation list attributes.
+ */
+public abstract class BaseParameterAnnotations extends BaseAttribute {
+    /** non-null; list of annotations */
+    private final AnnotationsList parameterAnnotations;
+
+    /** &gt;= 0; attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param attributeName non-null; the name of the attribute
+     * @param parameterAnnotations non-null; the annotations
+     * @param byteLength &gt;= 0; attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public BaseParameterAnnotations(String attributeName,
+            AnnotationsList parameterAnnotations, int byteLength) {
+        super(attributeName);
+
+        try {
+            if (parameterAnnotations.isMutable()) {
+                throw new MutabilityException(
+                        "parameterAnnotations.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("parameterAnnotations == null");
+        }
+
+        this.parameterAnnotations = parameterAnnotations;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the list of annotation lists associated with this instance.
+     * 
+     * @return non-null; the list
+     */
+    public final AnnotationsList getParameterAnnotations() {
+        return parameterAnnotations;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/InnerClassList.java b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
new file mode 100644
index 0000000..3585f1d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "inner class" entries, which are the contents of
+ * <code>InnerClasses</code> attributes.
+ */
+public final class InnerClassList extends FixedSizeList {
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the list of inner classes
+     */
+    public InnerClassList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n &gt;= 0; which item
+     * @return null-ok; the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which class
+     * @param innerClass non-null; class this item refers to
+     * @param outerClass null-ok; outer class that this class is a
+     * member of, if any
+     * @param innerName null-ok; original simple name of this class,
+     * if not anonymous
+     * @param accessFlags original declared access flags
+     */
+    public void set(int n, CstType innerClass, CstType outerClass,
+                    CstUtf8 innerName, int accessFlags) {
+        set0(n, new Item(innerClass, outerClass, innerName, accessFlags));
+    }
+
+    /**
+     * Item in an inner classes list.
+     */
+    public static class Item {
+        /** non-null; class this item refers to */
+        private final CstType innerClass;
+
+        /** null-ok; outer class that this class is a member of, if any */
+        private final CstType outerClass;
+
+        /** null-ok; original simple name of this class, if not anonymous */
+        private final CstUtf8 innerName;
+
+        /** original declared access flags */
+        private final int accessFlags;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param innerClass non-null; class this item refers to
+         * @param outerClass null-ok; outer class that this class is a
+         * member of, if any
+         * @param innerName null-ok; original simple name of this
+         * class, if not anonymous
+         * @param accessFlags original declared access flags
+         */
+        public Item(CstType innerClass, CstType outerClass,
+                    CstUtf8 innerName, int accessFlags) {
+            if (innerClass == null) {
+                throw new NullPointerException("innerClass == null");
+            }
+
+            this.innerClass = innerClass;
+            this.outerClass = outerClass;
+            this.innerName = innerName;
+            this.accessFlags = accessFlags;
+        }
+
+        /**
+         * Gets the class this item refers to.
+         *
+         * @return non-null; the class
+         */
+        public CstType getInnerClass() {
+            return innerClass;
+        }
+
+        /**
+         * Gets the outer class that this item's class is a member of, if any.
+         *
+         * @return null-ok; the class
+         */
+        public CstType getOuterClass() {
+            return outerClass;
+        }
+
+        /**
+         * Gets the original name of this item's class, if not anonymous.
+         *
+         * @return null-ok; the name
+         */
+        public CstUtf8 getInnerName() {
+            return innerName;
+        }
+
+        /**
+         * Gets the original declared access flags.
+         *
+         * @return the access flags
+         */
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/RawAttribute.java b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
new file mode 100644
index 0000000..b89926d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.util.ByteArray;
+
+/**
+ * Raw attribute, for holding onto attributes that are unrecognized.
+ */
+public final class RawAttribute extends BaseAttribute {
+    /** non-null; attribute data */
+    private final ByteArray data;
+
+    /**
+     * null-ok; constant pool to use for resolution of cpis in {@link
+     * #data} 
+     */
+    private final ConstantPool pool;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param name non-null; attribute name
+     * @param data non-null; attribute data
+     * @param pool null-ok; constant pool to use for cpi resolution
+     */
+    public RawAttribute(String name, ByteArray data, ConstantPool pool) {
+        super(name);
+
+        if (data == null) {
+            throw new NullPointerException("data == null");
+        }
+
+        this.data = data;
+        this.pool = pool;
+    }
+
+    /**
+     * Constructs an instance from a sub-array of a {@link ByteArray}.
+     * 
+     * @param name non-null; attribute name
+     * @param data non-null; array containing the attribute data
+     * @param offset offset in <code>data</code> to the attribute data
+     * @param length length of the attribute data, in bytes
+     * @param pool null-ok; constant pool to use for cpi resolution
+     */
+    public RawAttribute(String name, ByteArray data, int offset,
+                        int length, ConstantPool pool) {
+        this(name, data.slice(offset, offset + length), pool);
+    }
+
+    /**
+     * Get the raw data of the attribute.
+     * 
+     * @return non-null; the data
+     */
+    public ByteArray getData() {
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return data.size() + 6;
+    }
+
+    /**
+     * Gets the constant pool to use for cpi resolution, if any. It
+     * presumably came from the class file that this attribute came
+     * from.
+     * 
+     * @return null-ok; the constant pool
+     */
+    public ConstantPool getPool() {
+        return pool;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/package.html b/dx/src/com/android/dx/cf/attrib/package.html
new file mode 100644
index 0000000..8125079
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/package.html
@@ -0,0 +1,11 @@
+<body>
+<p>Implementation of containers and utilities for all the standard Java
+attribute types.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/code/BaseMachine.java b/dx/src/com/android/dx/cf/code/BaseMachine.java
new file mode 100644
index 0000000..430e48b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BaseMachine.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import java.util.ArrayList;
+
+/**
+ * Base implementation of {@link Machine}.
+ * 
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class BaseMachine implements Machine {
+    /* non-null; the prototype for the associated method */
+    private final Prototype prototype;
+    
+    /** non-null; primary arguments */
+    private TypeBearer[] args;
+
+    /** &gt;= 0; number of primary arguments */
+    private int argCount;
+
+    /** null-ok; type of the operation, if salient */
+    private Type auxType;
+
+    /** auxiliary <code>int</code> argument */
+    private int auxInt;
+
+    /** null-ok; auxiliary constant argument */
+    private Constant auxCst;
+
+    /** auxiliary branch target argument */
+    private int auxTarget;
+
+    /** null-ok; auxiliary switch cases argument */
+    private SwitchList auxCases;
+
+    /** null-ok; auxiliary initial value list for newarray */
+    private ArrayList<Constant> auxInitValues;
+
+    /** &gt;= -1; last local accessed */
+    private int localIndex;
+
+    /** null-ok; local target spec, if salient and calculated */
+    private RegisterSpec localTarget;
+
+    /** non-null; results */
+    private TypeBearer[] results;
+
+    /**
+     * &gt;= -1; count of the results, or <code>-1</code> if no results
+     * have been set
+     */
+    private int resultCount;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param prototype non-null; the prototype for the associated method
+     */
+    public BaseMachine(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        this.prototype = prototype;
+        args = new TypeBearer[10];
+        results = new TypeBearer[6];
+        clearArgs();
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getPrototype() {
+        return prototype;
+    }
+
+    /** {@inheritDoc} */
+    public final void clearArgs() {
+        argCount = 0;
+        auxType = null;
+        auxInt = 0;
+        auxCst = null;
+        auxTarget = 0;
+        auxCases = null;
+        auxInitValues = null;
+        localIndex = -1;
+        localTarget = null;
+        resultCount = -1;
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, int count) {
+        ExecutionStack stack = frame.getStack();
+
+        clearArgs();
+
+        if (count > args.length) {
+            // Grow args, and add a little extra room to grow even more.
+            args = new TypeBearer[count + 10];
+        }
+
+        for (int i = count - 1; i >= 0; i--) {
+            args[i] = stack.pop();
+        }
+
+        argCount = count;
+    }
+
+    /** {@inheritDoc} */
+    public void popArgs(Frame frame, Prototype prototype) {
+        StdTypeList types = prototype.getParameterTypes();
+        int size = types.size();
+        
+        // Use the above method to do the actual popping...
+        popArgs(frame, size);
+
+        // ...and then verify the popped types.
+
+        for (int i = 0; i < size; i++) {
+            if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
+                throw new SimException("at stack depth " + (size - 1 - i) +
+                        ", expected type " + types.getType(i).toHuman() +
+                        " but found " + args[i].getType().toHuman());
+            }
+        }
+    }
+
+    public final void popArgs(Frame frame, Type type) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 1);
+
+        // ...and then verify the popped type.
+        if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
+            throw new SimException("expected type " + type.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, Type type1, Type type2) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 2);
+
+        // ...and then verify the popped types.
+
+        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+            throw new SimException("expected type " + type1.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[1].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, Type type1, Type type2,
+            Type type3) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 3);
+
+        // ...and then verify the popped types.
+
+        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+            throw new SimException("expected type " + type1.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[1].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[2].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void localArg(Frame frame, int idx) {
+        clearArgs();
+        args[0] = frame.getLocals().get(idx);
+        argCount = 1;
+        localIndex = idx;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxType(Type type) {
+        auxType = type;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxIntArg(int value) {
+        auxInt = value;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxCstArg(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        auxCst = cst;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxTargetArg(int target) {
+        auxTarget = target;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxSwitchArg(SwitchList cases) {
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        auxCases = cases;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxInitValues(ArrayList<Constant> initValues) {
+        auxInitValues = initValues;
+    }
+
+    /** {@inheritDoc} */
+    public final void localTarget(int idx, Type type, LocalItem local) {
+        localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
+    }
+
+    /**
+     * Gets the number of primary arguments.
+     * 
+     * @return &gt;= 0; the number of primary arguments
+     */
+    protected final int argCount() {
+        return argCount;
+    }
+
+    /**
+     * Gets the width of the arguments (where a category-2 value counts as
+     * two).
+     * 
+     * @return &gt;= 0; the argument width
+     */
+    protected final int argWidth() {
+        int result = 0;
+
+        for (int i = 0; i < argCount; i++) {
+            result += args[i].getType().getCategory();
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the <code>n</code>th primary argument.
+     * 
+     * @param n &gt;= 0, &lt; argCount(); which argument
+     * @return non-null; the indicated argument
+     */
+    protected final TypeBearer arg(int n) {
+        if (n >= argCount) {
+            throw new IllegalArgumentException("n >= argCount");
+        }
+
+        try {
+            return args[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("n < 0");
+        }
+    }
+
+    /**
+     * Gets the type auxiliary argument.
+     * 
+     * @return null-ok; the salient type
+     */
+    protected final Type getAuxType() {
+        return auxType;
+    }
+
+    /**
+     * Gets the <code>int</code> auxiliary argument.
+     * 
+     * @return the argument value
+     */
+    protected final int getAuxInt() {
+        return auxInt;
+    }
+
+    /**
+     * Gets the constant auxiliary argument.
+     * 
+     * @return null-ok; the argument value
+     */
+    protected final Constant getAuxCst() {
+        return auxCst;
+    }
+
+    /**
+     * Gets the branch target auxiliary argument.
+     * 
+     * @return the argument value
+     */
+    protected final int getAuxTarget() {
+        return auxTarget;
+    }
+
+    /**
+     * Gets the switch cases auxiliary argument.
+     * 
+     * @return null-ok; the argument value
+     */
+    protected final SwitchList getAuxCases() {
+        return auxCases;
+    }
+
+    /**
+     * Gets the init values auxiliary argument.
+     *
+     * @return null-ok; the argument value
+     */
+    protected final ArrayList<Constant> getInitValues() {
+        return auxInitValues;
+    }
+    /**
+     * Gets the last local index accessed.
+     * 
+     * @return &gt;= -1; the salient local index or <code>-1</code> if none
+     * was set since the last time {@link #clearArgs} was called
+     */
+    protected final int getLocalIndex() {
+        return localIndex;
+    }
+
+    /**
+     * Gets the target local register spec of the current operation, if any.
+     * The local target spec is the combination of the values indicated
+     * by a previous call to {@link #localTarget} with the type of what
+     * should be the sole result set by a call to {@link #setResult} (or
+     * the combination {@link #clearResult} then {@link #addResult}.
+     * 
+     * @return null-ok; the salient register spec or <code>null</code> if no
+     * local target was set since the last time {@link #clearArgs} was
+     * called
+     */
+    protected final RegisterSpec getLocalTarget() {
+        if (localTarget == null) {
+            return null;
+        }
+
+        if (resultCount != 1) {
+            throw new SimException("local target with " + 
+                    ((resultCount == 0) ? "no" : "multiple") + " results");
+        }
+
+        TypeBearer result = results[0];
+        Type resultType = result.getType();
+        Type localType = localTarget.getType();
+
+        if (resultType == localType) {
+            return localTarget;
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
+            // The result and local types are inconsistent. Complain!
+            throwLocalMismatch(resultType, localType);
+            return null;
+        }
+
+        if (localType == Type.OBJECT) {
+            /*
+             * The result type is more specific than the local type,
+             * so use that instead.
+             */
+            localTarget = localTarget.withType(result);
+        }
+
+        return localTarget;
+    }
+
+    /**
+     * Clears the results.
+     */
+    protected final void clearResult() {
+        resultCount = 0;
+    }
+
+    /**
+     * Sets the results list to be the given single value.
+     * 
+     * <p><b>Note:</b> If there is more than one result value, the
+     * others may be added by using {@link #addResult}.</p>
+     * 
+     * @param result non-null; result value
+     */
+    protected final void setResult(TypeBearer result) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        results[0] = result;
+        resultCount = 1;
+    }
+
+    /**
+     * Adds an additional element to the list of results.
+     * 
+     * @see #setResult
+     * 
+     * @param result non-null; result value
+     */
+    protected final void addResult(TypeBearer result) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        results[resultCount] = result;
+        resultCount++;
+    }
+
+    /**
+     * Gets the count of results. This throws an exception if results were
+     * never set. (Explicitly clearing the results counts as setting them.)
+     * 
+     * @return &gt;= 0; the count
+     */
+    protected final int resultCount() {
+        if (resultCount < 0) {
+            throw new SimException("results never set");
+        }
+
+        return resultCount;
+    }
+
+    /**
+     * Gets the width of the results (where a category-2 value counts as
+     * two).
+     * 
+     * @return &gt;= 0; the result width
+     */
+    protected final int resultWidth() {
+        int width = 0;
+
+        for (int i = 0; i < resultCount; i++) {
+            width += results[i].getType().getCategory();
+        }
+
+        return width;
+    }
+
+    /**
+     * Gets the <code>n</code>th result value.
+     * 
+     * @param n &gt;= 0, &lt; resultCount(); which result
+     * @return non-null; the indicated result value
+     */
+    protected final TypeBearer result(int n) {
+        if (n >= resultCount) {
+            throw new IllegalArgumentException("n >= resultCount");
+        }
+
+        try {
+            return results[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("n < 0");
+        }
+    }
+
+    /**
+     * Stores the results of the latest operation into the given frame. If
+     * there is a local target (see {@link #localTarget}), then the sole
+     * result is stored to that target; otherwise any results are pushed
+     * onto the stack.
+     * 
+     * @param frame non-null; frame to operate on
+     */
+    protected final void storeResults(Frame frame) {
+        if (resultCount < 0) {
+            throw new SimException("results never set");
+        }
+
+        if (resultCount == 0) {
+            // Nothing to do.
+            return;
+        }
+
+        if (localTarget != null) {
+            /*
+             * Note: getLocalTarget() doesn't necessarily return
+             * localTarget directly.
+             */
+            frame.getLocals().set(getLocalTarget());
+        } else {
+            ExecutionStack stack = frame.getStack();
+            for (int i = 0; i < resultCount; i++) {
+                stack.push(results[i]);
+            }
+        }
+    }
+
+    /**
+     * Throws an exception that indicates a mismatch in local variable
+     * types.
+     * 
+     * @param found non-null; the encountered type
+     * @param local non-null; the local variable's claimed type
+     */
+    public static void throwLocalMismatch(TypeBearer found,
+            TypeBearer local) {
+        throw new SimException("local variable type mismatch: " +
+                "attempt to set or access a value of type " +
+                found.toHuman() + 
+                " using a local variable of type " + 
+                local.toHuman() +
+                ". This is symptomatic of .class transformation tools " +
+                "that ignore local variable information.");
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/BasicBlocker.java b/dx/src/com/android/dx/cf/code/BasicBlocker.java
new file mode 100644
index 0000000..82e4a08
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BasicBlocker.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+import java.util.ArrayList;
+
+/**
+ * Utility that identifies basic blocks in bytecode.
+ */
+public final class BasicBlocker implements BytecodeArray.Visitor {
+    /** non-null; method being converted */
+    private final ConcreteMethod method;
+
+    /** non-null; work set; bits indicate offsets in need of examination */
+    private final int[] workSet;
+
+    /**
+     * non-null; live set; bits indicate potentially-live opcodes; contrawise,
+     * a bit that isn't on is either in the middle of an instruction or is
+     * a definitely-dead opcode 
+     */
+    private final int[] liveSet;
+
+    /**
+     * non-null; block start set; bits indicate the starts of basic blocks,
+     * including the opcodes that start blocks of definitely-dead code 
+     */
+    private final int[] blockSet;
+
+    /**
+     * non-null, sparse; for each instruction offset to a branch of
+     * some sort, the list of targets for that instruction 
+     */
+    private final IntList[] targetLists;
+
+    /**
+     * non-null, sparse; for each instruction offset to a throwing
+     * instruction, the list of exception handlers for that instruction 
+     */
+    private final ByteCatchList[] catchLists;
+
+    /** offset of the previously parsed bytecode */
+    private int previousOffset;
+
+    /**
+     * Identifies and enumerates the basic blocks in the given method,
+     * returning a list of them. The returned list notably omits any
+     * definitely-dead code that is identified in the process.
+     * 
+     * @param method non-null; method to convert
+     * @return non-null; list of basic blocks
+     */
+    public static ByteBlockList identifyBlocks(ConcreteMethod method) {
+        BasicBlocker bb = new BasicBlocker(method);
+
+        bb.doit();
+        return bb.getBlockList();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable; use
+     * {@link #identifyBlocks}.
+     * 
+     * @param method non-null; method to convert
+     */
+    private BasicBlocker(ConcreteMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.method = method;
+
+        /*
+         * The "+1" below is so the idx-past-end is also valid,
+         * avoiding a special case, but without preventing
+         * flow-of-control falling past the end of the method from
+         * getting properly reported.
+         */
+        int sz = method.getCode().size() + 1;
+
+        workSet = Bits.makeBitSet(sz);
+        liveSet = Bits.makeBitSet(sz);
+        blockSet = Bits.makeBitSet(sz);
+        targetLists = new IntList[sz];
+        catchLists = new ByteCatchList[sz];
+        previousOffset = -1;
+    }
+
+    /*
+     * Note: These methods are defined implementation of the interface
+     * BytecodeArray.Visitor; since the class isn't publicly
+     * instantiable, no external code ever gets a chance to actually
+     * call these methods.
+     */
+
+    /** {@inheritDoc} */
+    public void visitInvalid(int opcode, int offset, int length) {
+        visitCommon(offset, length, true);
+    }
+
+    /** {@inheritDoc} */
+    public void visitNoArgs(int opcode, int offset, int length, Type type) {
+        switch (opcode) {
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN: {
+                visitCommon(offset, length, false);
+                targetLists[offset] = IntList.EMPTY;
+                break;
+            }
+            case ByteOps.ATHROW: {
+                visitCommon(offset, length, false);
+                visitThrowing(offset, length, false);
+                break;
+            }
+            case ByteOps.IALOAD:
+            case ByteOps.LALOAD:
+            case ByteOps.FALOAD:
+            case ByteOps.DALOAD:
+            case ByteOps.AALOAD:
+            case ByteOps.BALOAD:
+            case ByteOps.CALOAD:
+            case ByteOps.SALOAD:
+            case ByteOps.IASTORE:
+            case ByteOps.LASTORE:
+            case ByteOps.FASTORE:
+            case ByteOps.DASTORE:
+            case ByteOps.AASTORE:
+            case ByteOps.BASTORE:
+            case ByteOps.CASTORE:
+            case ByteOps.SASTORE:
+            case ByteOps.ARRAYLENGTH:
+            case ByteOps.MONITORENTER:
+            case ByteOps.MONITOREXIT: {
+                /*
+                 * These instructions can all throw, so they have to end
+                 * the block they appear in (since throws are branches).
+                 */
+                visitCommon(offset, length, true);
+                visitThrowing(offset, length, true);
+                break;
+            }
+            case ByteOps.IDIV:
+            case ByteOps.IREM: {
+                /*
+                 * The int and long versions of division and remainder may
+                 * throw, but not the other types.
+                 */
+                visitCommon(offset, length, true);
+                if ((type == Type.INT) || (type == Type.LONG)) {
+                    visitThrowing(offset, length, true);
+                }
+                break;                
+            }
+            default: {
+                visitCommon(offset, length, true);
+                break;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitLocal(int opcode, int offset, int length,
+            int idx, Type type, int value) {
+        if (opcode == ByteOps.RET) {
+            visitCommon(offset, length, false);
+            targetLists[offset] = IntList.EMPTY;
+        } else {
+            visitCommon(offset, length, true);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitConstant(int opcode, int offset, int length,
+            Constant cst, int value) {
+        visitCommon(offset, length, true);
+
+        if ((cst instanceof CstMemberRef) || (cst instanceof CstType) ||
+            (cst instanceof CstString)) {
+            /*
+             * Instructions with these sorts of constants have the
+             * possibility of throwing, so this instruction needs to
+             * end its block (since it can throw, and possible-throws
+             * are branch points).
+             */
+            visitThrowing(offset, length, true);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitBranch(int opcode, int offset, int length,
+            int target) {
+        switch (opcode) {
+            case ByteOps.GOTO: {
+                visitCommon(offset, length, false);
+                targetLists[offset] = IntList.makeImmutable(target);
+                break;
+            }
+            case ByteOps.JSR: {
+                /*
+                 * Each jsr is quarantined into a separate block (containing
+                 * only the jsr instruction) but is otherwise treated
+                 * as a conditional branch. (That is to say, both its
+                 * target and next instruction begin new blocks.)
+                 */
+                addWorkIfNecessary(offset, true);
+                // Fall through to next case...
+            }
+            default: {
+                int next = offset + length;
+                visitCommon(offset, length, true);
+                addWorkIfNecessary(next, true);
+                targetLists[offset] = IntList.makeImmutable(next, target);
+                break;
+            }
+        }
+
+        addWorkIfNecessary(target, true);
+    }
+
+    /** {@inheritDoc} */
+    public void visitSwitch(int opcode, int offset, int length,
+            SwitchList cases, int padding) {
+        visitCommon(offset, length, false);
+        addWorkIfNecessary(cases.getDefaultTarget(), true);
+
+        int sz = cases.size();
+        for (int i = 0; i < sz; i++) {
+            addWorkIfNecessary(cases.getTarget(i), true);
+        }
+
+        targetLists[offset] = cases.getTargets();
+    }
+
+    /** {@inheritDoc} */
+    public void visitNewarray(int offset, int length, CstType type,
+            ArrayList<Constant> intVals) {
+        visitCommon(offset, length, true);
+        visitThrowing(offset, length, true);
+    }
+
+    /**
+     * Extracts the list of basic blocks from the bit sets.
+     * 
+     * @return non-null; the list of basic blocks
+     */
+    private ByteBlockList getBlockList() {
+        ByteCatchList catches = method.getCatches();
+        BytecodeArray bytes = method.getCode();
+        ByteBlock[] bbs = new ByteBlock[bytes.size()];
+        int count = 0;
+
+        for (int at = 0, next; /*at*/; at = next) {
+            next = Bits.findFirst(blockSet, at + 1);
+            if (next < 0) {
+                break;
+            }
+
+            if (Bits.get(liveSet, at)) {
+                /*
+                 * Search backward for the branch or throwing
+                 * instruction at the end of this block, if any. If
+                 * there isn't any, then "next" is the sole target.
+                 */
+                IntList targets = null;
+                int targetsAt = -1;
+                ByteCatchList blockCatches;
+
+                for (int i = next - 1; i >= at; i--) {
+                    targets = targetLists[i];
+                    if (targets != null) {
+                        targetsAt = i;
+                        break;
+                    }
+                }
+
+                if (targets == null) {
+                    targets = IntList.makeImmutable(next);
+                    blockCatches = ByteCatchList.EMPTY;
+                } else {
+                    blockCatches = catchLists[targetsAt];
+                    if (blockCatches == null) {
+                        blockCatches = ByteCatchList.EMPTY;
+                    }
+                }
+
+                bbs[count] =
+                    new ByteBlock(at, at, next, targets, blockCatches);
+                count++;
+            }
+        }
+
+        ByteBlockList result = new ByteBlockList(count);
+        for (int i = 0; i < count; i++) {
+            result.set(i, bbs[i]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Does basic block identification.
+     */
+    private void doit() {
+        BytecodeArray bytes = method.getCode();
+        ByteCatchList catches = method.getCatches();
+        int catchSz = catches.size();
+
+        /*
+         * Start by setting offset 0 as the start of a block and in need
+         * of work...
+         */
+        Bits.set(workSet, 0);
+        Bits.set(blockSet, 0);
+
+        /*
+         * And then process the work set, add new work based on
+         * exception ranges that are active, and iterate until there's
+         * nothing left to work on.
+         */
+        while (!Bits.isEmpty(workSet)) {
+            try {
+                bytes.processWorkSet(workSet, this);
+            } catch (IllegalArgumentException ex) {
+                // Translate the exception.
+                throw new SimException("flow of control falls off " +
+                                       "end of method",
+                                       ex);
+            }
+
+            for (int i = 0; i < catchSz; i++) {
+                ByteCatchList.Item item = catches.get(i);
+                int start = item.getStartPc();
+                int end = item.getEndPc();
+                if (Bits.anyInRange(liveSet, start, end)) {
+                    Bits.set(blockSet, start);
+                    Bits.set(blockSet, end);
+                    addWorkIfNecessary(item.getHandlerPc(), true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets a bit in the work set, but only if the instruction in question
+     * isn't yet known to be possibly-live.
+     * 
+     * @param offset offset to the instruction in question
+     * @param blockStart <code>true</code> iff this instruction starts a
+     * basic block
+     */
+    private void addWorkIfNecessary(int offset, boolean blockStart) {
+        if (!Bits.get(liveSet, offset)) {
+            Bits.set(workSet, offset);
+        }
+
+        if (blockStart) {
+            Bits.set(blockSet, offset);
+        }
+    }
+
+    /**
+     * Helper method used by all the visitor methods.
+     * 
+     * @param offset offset to the instruction
+     * @param length length of the instruction, in bytes
+     * @param nextIsLive <code>true</code> iff the instruction after
+     * the indicated one is possibly-live (because this one isn't an
+     * unconditional branch, a return, or a switch)
+     */
+    private void visitCommon(int offset, int length, boolean nextIsLive) {
+        Bits.set(liveSet, offset);
+
+        if (nextIsLive) {
+            /*
+             * If the next instruction is flowed to by this one, just
+             * add it to the work set, and then a subsequent visit*()
+             * will deal with it as appropriate.
+             */
+            addWorkIfNecessary(offset + length, false);
+        } else {
+            /*
+             * If the next instruction isn't flowed to by this one,
+             * then mark it as a start of a block but *don't* add it
+             * to the work set, so that in the final phase we can know
+             * dead code blocks as those marked as blocks but not also marked
+             * live.
+             */
+            Bits.set(blockSet, offset + length);
+        }
+    }
+
+    /**
+     * Helper method used by all the visitor methods that deal with
+     * opcodes that possibly throw. This method should be called after calling
+     * {@link #visitCommon}.
+     * 
+     * @param offset offset to the instruction
+     * @param length length of the instruction, in bytes
+     * @param nextIsLive <code>true</code> iff the instruction after
+     * the indicated one is possibly-live (because this one isn't an
+     * unconditional throw)
+     */
+    private void visitThrowing(int offset, int length, boolean nextIsLive) {
+        int next = offset + length;
+
+        if (nextIsLive) {
+            addWorkIfNecessary(next, true);
+        }
+
+        ByteCatchList catches = method.getCatches().listFor(offset);
+        catchLists[offset] = catches;
+        targetLists[offset] = catches.toTargetList(nextIsLive ? next : -1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setPreviousOffset(int offset) {
+        previousOffset = offset;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getPreviousOffset() {
+        return previousOffset;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlock.java b/dx/src/com/android/dx/cf/code/ByteBlock.java
new file mode 100644
index 0000000..065c522
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlock.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Representation of a basic block in a bytecode array.
+ */
+public final class ByteBlock implements LabeledItem {
+    /** &gt;= 0; label for this block */
+    private final int label;
+
+    /** &gt;= 0; bytecode offset (inclusive) of the start of the block */
+    private final int start;
+
+    /** &gt; start; bytecode offset (exclusive) of the end of the block */
+    private final int end;
+
+    /** non-null; list of successors that this block may branch to */
+    private final IntList successors;
+
+    /** non-null; list of exceptions caught and their handler targets */
+    private final ByteCatchList catches;
+
+    /**
+     * Constructs an instance. 
+     * 
+     * @param label &gt;= 0; target label for this block
+     * @param start &gt;= 0; bytecode offset (inclusive) of the start
+     * of the block
+     * @param end &gt; start; bytecode offset (exclusive) of the end
+     * of the block
+     * @param successors non-null; list of successors that this block may
+     * branch to
+     * @param catches non-null; list of exceptions caught and their
+     * handler targets
+     */
+    public ByteBlock(int label, int start, int end, IntList successors,
+                     ByteCatchList catches) {
+        if (label < 0) {
+            throw new IllegalArgumentException("label < 0");
+        }
+
+        if (start < 0) {
+            throw new IllegalArgumentException("start < 0");
+        }
+
+        if (end <= start) {
+            throw new IllegalArgumentException("end <= start");
+        }
+
+        if (successors == null) {
+            throw new NullPointerException("targets == null");
+        }
+
+        int sz = successors.size();
+        for (int i = 0; i < sz; i++) {
+            if (successors.get(i) < 0) {
+                throw new IllegalArgumentException("successors[" + i +
+                                                   "] == " +
+                                                   successors.get(i));
+            }
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.label = label;
+        this.start = start;
+        this.end = end;
+        this.successors = successors;
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return '{' + Hex.u2(label) + ": " + Hex.u2(start) + ".." +
+            Hex.u2(end) + '}';
+    }
+
+    /**
+     * Gets the label of this block.
+     * 
+     * @return &gt;= 0; the label
+     */
+    public int getLabel() {
+        return label;
+    }
+
+    /**
+     * Gets the bytecode offset (inclusive) of the start of this block.
+     * 
+     * @return &gt;= 0; the start offset
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Gets the bytecode offset (exclusive) of the end of this block.
+     * 
+     * @return &gt; getStart(); the end offset
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Gets the list of successors that this block may branch to 
+     * non-exceptionally.
+     * 
+     * @return non-null; the successor list
+     */
+    public IntList getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * Gets the list of exceptions caught and their handler targets.
+     * 
+     * @return non-null; the catch list
+     */
+    public ByteCatchList getCatches() {
+        return catches;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlockList.java b/dx/src/com/android/dx/cf/code/ByteBlockList.java
new file mode 100644
index 0000000..9d27b7f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlockList.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link ByteBlock} instances.
+ */
+public final class ByteBlockList extends LabeledList {
+
+    /**
+     * Constructs an instance.
+     *
+     * @param size &gt;= 0; the number of elements to be in the list
+     */
+    public ByteBlockList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw <code>NullPointerException</code>.
+     *
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return non-null; the indicated element
+     */
+    public ByteBlock get(int n) {
+        return (ByteBlock) get0(n);
+    }
+
+    /**
+     * Gets the block with the given label.
+     *
+     * @param label the label to look for
+     * @return non-null; the block with the given label
+     */
+    public ByteBlock labelToBlock(int label) {
+        int idx = indexOfLabel(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label: "
+                    + Hex.u2(label));
+        }
+
+        return get(idx);
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param bb null-ok; the value to store
+     */
+    public void set(int n, ByteBlock bb) {
+        super.set(n, bb);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteCatchList.java b/dx/src/com/android/dx/cf/code/ByteCatchList.java
new file mode 100644
index 0000000..375831e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteCatchList.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IntList;
+
+/**
+ * List of catch entries, that is, the elements of an "exception table,"
+ * which is part of a standard <code>Code</code> attribute.
+ */
+public final class ByteCatchList extends FixedSizeList {
+    /** non-null; convenient zero-entry instance */
+    public static final ByteCatchList EMPTY = new ByteCatchList(0);
+
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the table
+     */
+    public ByteCatchList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the total length of this structure in bytes, when included in
+     * a <code>Code</code> attribute. The returned value includes the
+     * two bytes for <code>exception_table_length</code>.
+     *
+     * @return &gt;= 2; the total length, in bytes
+     */
+    public int byteLength() {
+        return 2 + size() * 8;
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n &gt;= 0; which item
+     * @return null-ok; the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which entry to set
+     * @param item non-null; the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which entry to set
+     * @param startPc &gt;= 0; the start pc (inclusive) of the handler's range
+     * @param endPc &gt;= startPc; the end pc (exclusive) of the
+     * handler's range
+     * @param handlerPc &gt;= 0; the pc of the exception handler
+     * @param exceptionClass null-ok; the exception class or
+     * <code>null</code> to catch all exceptions with this handler
+     */
+    public void set(int n, int startPc, int endPc, int handlerPc,
+            CstType exceptionClass) {
+        set0(n, new Item(startPc, endPc, handlerPc, exceptionClass));
+    }
+
+    /**
+     * Gets the list of items active at the given address. The result is
+     * automatically made immutable.
+     *
+     * @param pc which address
+     * @return non-null; list of exception handlers active at
+     * <code>pc</code>
+     */
+    public ByteCatchList listFor(int pc) {
+        int sz = size();
+        Item[] resultArr = new Item[sz];
+        int resultSz = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Item one = get(i);
+            if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) {
+                resultArr[resultSz] = one;
+                resultSz++;
+            }
+        }
+
+        if (resultSz == 0) {
+            return EMPTY;
+        }
+
+        ByteCatchList result = new ByteCatchList(resultSz);
+        for (int i = 0; i < resultSz; i++) {
+            result.set(i, resultArr[i]);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Helper method for {@link #listFor}, which tells whether a match
+     * is <i>not</i> found for the exception type of the given item in
+     * the given array. A match is considered to be either an exact type
+     * match or the class <code>Object</code> which represents a catch-all.
+     * 
+     * @param item non-null; item with the exception type to look for
+     * @param arr non-null; array to search in
+     * @param count non-null; maximum number of elements in the array to check
+     * @return <code>true</code> iff the exception type is <i>not</i> found
+     */
+    private static boolean typeNotFound(Item item, Item[] arr, int count) {
+        CstType type = item.getExceptionClass();
+
+        for (int i = 0; i < count; i++) {
+            CstType one = arr[i].getExceptionClass();
+            if ((one == type) || (one == CstType.OBJECT)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a target list corresponding to this instance. The result
+     * is a list of all the exception handler addresses, with the given
+     * <code>noException</code> address appended if appropriate. The
+     * result is automatically made immutable.
+     * 
+     * @param noException &gt;= -1; the no-exception address to append, or
+     * <code>-1</code> not to append anything
+     * @return non-null; list of exception targets, with
+     * <code>noException</code> appended if necessary
+     */
+    public IntList toTargetList(int noException) {
+        if (noException < -1) {
+            throw new IllegalArgumentException("noException < -1");
+        }
+
+        boolean hasDefault = (noException >= 0);
+        int sz = size();
+
+        if (sz == 0) {
+            if (hasDefault) {
+                /*
+                 * The list is empty, but there is a no-exception
+                 * address; so, the result is just that address.
+                 */
+                return IntList.makeImmutable(noException);
+            }
+            /*
+             * The list is empty and there isn't even a no-exception
+             * address.
+             */
+            return IntList.EMPTY;
+        }
+
+        IntList result = new IntList(sz + (hasDefault ? 1 : 0));
+
+        for (int i = 0; i < sz; i++) {
+            result.add(get(i).getHandlerPc());
+        }
+
+        if (hasDefault) {
+            result.add(noException);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Returns a rop-style catches list equivalent to this one.
+     *
+     * @return non-null; the converted instance
+     */
+    public TypeList toRopCatchList() {
+        int sz = size();
+        if (sz == 0) {
+            return StdTypeList.EMPTY;
+        }
+
+        StdTypeList result = new StdTypeList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.set(i, get(i).getExceptionClass().getClassType());
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Item in an exception handler list.
+     */
+    public static class Item {
+        /** &gt;= 0; the start pc (inclusive) of the handler's range */
+        private final int startPc;
+
+        /** &gt;= startPc; the end pc (exclusive) of the handler's range */
+        private final int endPc;
+
+        /** &gt;= 0; the pc of the exception handler */
+        private final int handlerPc;
+
+        /** null-ok; the exception class or <code>null</code> to catch all
+         * exceptions with this handler */
+        private final CstType exceptionClass;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param startPc &gt;= 0; the start pc (inclusive) of the
+         * handler's range
+         * @param endPc &gt;= startPc; the end pc (exclusive) of the
+         * handler's range
+         * @param handlerPc &gt;= 0; the pc of the exception handler
+         * @param exceptionClass null-ok; the exception class or
+         * <code>null</code> to catch all exceptions with this handler
+         */
+        public Item(int startPc, int endPc, int handlerPc,
+                CstType exceptionClass) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (endPc < startPc) {
+                throw new IllegalArgumentException("endPc < startPc");
+            }
+
+            if (handlerPc < 0) {
+                throw new IllegalArgumentException("handlerPc < 0");
+            }
+
+            this.startPc = startPc;
+            this.endPc = endPc;
+            this.handlerPc = handlerPc;
+            this.exceptionClass = exceptionClass;
+        }
+
+        /**
+         * Gets the start pc (inclusive) of the handler's range.
+         *
+         * @return &gt;= 0; the start pc (inclusive) of the handler's range.
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the end pc (exclusive) of the handler's range.
+         *
+         * @return &gt;= startPc; the end pc (exclusive) of the
+         * handler's range.
+         */
+        public int getEndPc() {
+            return endPc;
+        }
+
+        /**
+         * Gets the pc of the exception handler.
+         *
+         * @return &gt;= 0; the pc of the exception handler
+         */
+        public int getHandlerPc() {
+            return handlerPc;
+        }
+
+        /**
+         * Gets the class of exception handled.
+         *
+         * @return non-null; the exception class; {@link CstType#OBJECT}
+         * if this entry handles all possible exceptions
+         */
+        public CstType getExceptionClass() {
+            return (exceptionClass != null) ?
+                exceptionClass : CstType.OBJECT;
+        }
+
+        /**
+         * Returns whether the given address is in the range of this item.
+         *
+         * @param pc the address
+         * @return <code>true</code> iff this item covers <code>pc</code>
+         */
+        public boolean covers(int pc) {
+            return (pc >= startPc) && (pc < endPc);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteOps.java b/dx/src/com/android/dx/cf/code/ByteOps.java
new file mode 100644
index 0000000..4c420f6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteOps.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants and utility methods for dealing with bytecode arrays at an
+ * opcode level.
+ */
+public class ByteOps {
+    // one constant per opcode
+    public static final int NOP = 0x00;
+    public static final int ACONST_NULL = 0x01;
+    public static final int ICONST_M1 = 0x02;
+    public static final int ICONST_0 = 0x03;
+    public static final int ICONST_1 = 0x04;
+    public static final int ICONST_2 = 0x05;
+    public static final int ICONST_3 = 0x06;
+    public static final int ICONST_4 = 0x07;
+    public static final int ICONST_5 = 0x08;
+    public static final int LCONST_0 = 0x09;
+    public static final int LCONST_1 = 0x0a;
+    public static final int FCONST_0 = 0x0b;
+    public static final int FCONST_1 = 0x0c;
+    public static final int FCONST_2 = 0x0d;
+    public static final int DCONST_0 = 0x0e;
+    public static final int DCONST_1 = 0x0f;
+    public static final int BIPUSH = 0x10;
+    public static final int SIPUSH = 0x11;
+    public static final int LDC = 0x12;
+    public static final int LDC_W = 0x13;
+    public static final int LDC2_W = 0x14;
+    public static final int ILOAD = 0x15;
+    public static final int LLOAD = 0x16;
+    public static final int FLOAD = 0x17;
+    public static final int DLOAD = 0x18;
+    public static final int ALOAD = 0x19;
+    public static final int ILOAD_0 = 0x1a;
+    public static final int ILOAD_1 = 0x1b;
+    public static final int ILOAD_2 = 0x1c;
+    public static final int ILOAD_3 = 0x1d;
+    public static final int LLOAD_0 = 0x1e;
+    public static final int LLOAD_1 = 0x1f;
+    public static final int LLOAD_2 = 0x20;
+    public static final int LLOAD_3 = 0x21;
+    public static final int FLOAD_0 = 0x22;
+    public static final int FLOAD_1 = 0x23;
+    public static final int FLOAD_2 = 0x24;
+    public static final int FLOAD_3 = 0x25;
+    public static final int DLOAD_0 = 0x26;
+    public static final int DLOAD_1 = 0x27;
+    public static final int DLOAD_2 = 0x28;
+    public static final int DLOAD_3 = 0x29;
+    public static final int ALOAD_0 = 0x2a;
+    public static final int ALOAD_1 = 0x2b;
+    public static final int ALOAD_2 = 0x2c;
+    public static final int ALOAD_3 = 0x2d;
+    public static final int IALOAD = 0x2e;
+    public static final int LALOAD = 0x2f;
+    public static final int FALOAD = 0x30;
+    public static final int DALOAD = 0x31;
+    public static final int AALOAD = 0x32;
+    public static final int BALOAD = 0x33;
+    public static final int CALOAD = 0x34;
+    public static final int SALOAD = 0x35;
+    public static final int ISTORE = 0x36;
+    public static final int LSTORE = 0x37;
+    public static final int FSTORE = 0x38;
+    public static final int DSTORE = 0x39;
+    public static final int ASTORE = 0x3a;
+    public static final int ISTORE_0 = 0x3b;
+    public static final int ISTORE_1 = 0x3c;
+    public static final int ISTORE_2 = 0x3d;
+    public static final int ISTORE_3 = 0x3e;
+    public static final int LSTORE_0 = 0x3f;
+    public static final int LSTORE_1 = 0x40;
+    public static final int LSTORE_2 = 0x41;
+    public static final int LSTORE_3 = 0x42;
+    public static final int FSTORE_0 = 0x43;
+    public static final int FSTORE_1 = 0x44;
+    public static final int FSTORE_2 = 0x45;
+    public static final int FSTORE_3 = 0x46;
+    public static final int DSTORE_0 = 0x47;
+    public static final int DSTORE_1 = 0x48;
+    public static final int DSTORE_2 = 0x49;
+    public static final int DSTORE_3 = 0x4a;
+    public static final int ASTORE_0 = 0x4b;
+    public static final int ASTORE_1 = 0x4c;
+    public static final int ASTORE_2 = 0x4d;
+    public static final int ASTORE_3 = 0x4e;
+    public static final int IASTORE = 0x4f;
+    public static final int LASTORE = 0x50;
+    public static final int FASTORE = 0x51;
+    public static final int DASTORE = 0x52;
+    public static final int AASTORE = 0x53;
+    public static final int BASTORE = 0x54;
+    public static final int CASTORE = 0x55;
+    public static final int SASTORE = 0x56;
+    public static final int POP = 0x57;
+    public static final int POP2 = 0x58;
+    public static final int DUP = 0x59;
+    public static final int DUP_X1 = 0x5a;
+    public static final int DUP_X2 = 0x5b;
+    public static final int DUP2 = 0x5c;
+    public static final int DUP2_X1 = 0x5d;
+    public static final int DUP2_X2 = 0x5e;
+    public static final int SWAP = 0x5f;
+    public static final int IADD = 0x60;
+    public static final int LADD = 0x61;
+    public static final int FADD = 0x62;
+    public static final int DADD = 0x63;
+    public static final int ISUB = 0x64;
+    public static final int LSUB = 0x65;
+    public static final int FSUB = 0x66;
+    public static final int DSUB = 0x67;
+    public static final int IMUL = 0x68;
+    public static final int LMUL = 0x69;
+    public static final int FMUL = 0x6a;
+    public static final int DMUL = 0x6b;
+    public static final int IDIV = 0x6c;
+    public static final int LDIV = 0x6d;
+    public static final int FDIV = 0x6e;
+    public static final int DDIV = 0x6f;
+    public static final int IREM = 0x70;
+    public static final int LREM = 0x71;
+    public static final int FREM = 0x72;
+    public static final int DREM = 0x73;
+    public static final int INEG = 0x74;
+    public static final int LNEG = 0x75;
+    public static final int FNEG = 0x76;
+    public static final int DNEG = 0x77;
+    public static final int ISHL = 0x78;
+    public static final int LSHL = 0x79;
+    public static final int ISHR = 0x7a;
+    public static final int LSHR = 0x7b;
+    public static final int IUSHR = 0x7c;
+    public static final int LUSHR = 0x7d;
+    public static final int IAND = 0x7e;
+    public static final int LAND = 0x7f;
+    public static final int IOR = 0x80;
+    public static final int LOR = 0x81;
+    public static final int IXOR = 0x82;
+    public static final int LXOR = 0x83;
+    public static final int IINC = 0x84;
+    public static final int I2L = 0x85;
+    public static final int I2F = 0x86;
+    public static final int I2D = 0x87;
+    public static final int L2I = 0x88;
+    public static final int L2F = 0x89;
+    public static final int L2D = 0x8a;
+    public static final int F2I = 0x8b;
+    public static final int F2L = 0x8c;
+    public static final int F2D = 0x8d;
+    public static final int D2I = 0x8e;
+    public static final int D2L = 0x8f;
+    public static final int D2F = 0x90;
+    public static final int I2B = 0x91;
+    public static final int I2C = 0x92;
+    public static final int I2S = 0x93;
+    public static final int LCMP = 0x94;
+    public static final int FCMPL = 0x95;
+    public static final int FCMPG = 0x96;
+    public static final int DCMPL = 0x97;
+    public static final int DCMPG = 0x98;
+    public static final int IFEQ = 0x99;
+    public static final int IFNE = 0x9a;
+    public static final int IFLT = 0x9b;
+    public static final int IFGE = 0x9c;
+    public static final int IFGT = 0x9d;
+    public static final int IFLE = 0x9e;
+    public static final int IF_ICMPEQ = 0x9f;
+    public static final int IF_ICMPNE = 0xa0;
+    public static final int IF_ICMPLT = 0xa1;
+    public static final int IF_ICMPGE = 0xa2;
+    public static final int IF_ICMPGT = 0xa3;
+    public static final int IF_ICMPLE = 0xa4;
+    public static final int IF_ACMPEQ = 0xa5;
+    public static final int IF_ACMPNE = 0xa6;
+    public static final int GOTO = 0xa7;
+    public static final int JSR = 0xa8;
+    public static final int RET = 0xa9;
+    public static final int TABLESWITCH = 0xaa;
+    public static final int LOOKUPSWITCH = 0xab;
+    public static final int IRETURN = 0xac;
+    public static final int LRETURN = 0xad;
+    public static final int FRETURN = 0xae;
+    public static final int DRETURN = 0xaf;
+    public static final int ARETURN = 0xb0;
+    public static final int RETURN = 0xb1;
+    public static final int GETSTATIC = 0xb2;
+    public static final int PUTSTATIC = 0xb3;
+    public static final int GETFIELD = 0xb4;
+    public static final int PUTFIELD = 0xb5;
+    public static final int INVOKEVIRTUAL = 0xb6;
+    public static final int INVOKESPECIAL = 0xb7;
+    public static final int INVOKESTATIC = 0xb8;
+    public static final int INVOKEINTERFACE = 0xb9;
+    public static final int NEW = 0xbb;
+    public static final int NEWARRAY = 0xbc;
+    public static final int ANEWARRAY = 0xbd;
+    public static final int ARRAYLENGTH = 0xbe;
+    public static final int ATHROW = 0xbf;
+    public static final int CHECKCAST = 0xc0;
+    public static final int INSTANCEOF = 0xc1;
+    public static final int MONITORENTER = 0xc2;
+    public static final int MONITOREXIT = 0xc3;
+    public static final int WIDE = 0xc4;
+    public static final int MULTIANEWARRAY = 0xc5;
+    public static final int IFNULL = 0xc6;
+    public static final int IFNONNULL = 0xc7;
+    public static final int GOTO_W = 0xc8;
+    public static final int JSR_W = 0xc9;
+
+    // a constant for each valid argument to "newarray"
+
+    public static final int NEWARRAY_BOOLEAN = 4;
+    public static final int NEWARRAY_CHAR = 5;
+    public static final int NEWARRAY_FLOAT = 6;
+    public static final int NEWARRAY_DOUBLE = 7;
+    public static final int NEWARRAY_BYTE = 8;
+    public static final int NEWARRAY_SHORT = 9;
+    public static final int NEWARRAY_INT = 10;
+    public static final int NEWARRAY_LONG = 11;
+
+    // a constant for each possible instruction format
+
+    /** invalid */
+    public static final int FMT_INVALID = 0;
+
+    /** "-": <code>op</code> */
+    public static final int FMT_NO_ARGS = 1;
+
+    /** "0": <code>op</code>; implies <code>max_locals &gt;= 1</code> */
+    public static final int FMT_NO_ARGS_LOCALS_1 = 2;
+
+    /** "1": <code>op</code>; implies <code>max_locals &gt;= 2</code> */
+    public static final int FMT_NO_ARGS_LOCALS_2 = 3;
+
+    /** "2": <code>op</code>; implies <code>max_locals &gt;= 3</code> */
+    public static final int FMT_NO_ARGS_LOCALS_3 = 4;
+
+    /** "3": <code>op</code>; implies <code>max_locals &gt;= 4</code> */
+    public static final int FMT_NO_ARGS_LOCALS_4 = 5;
+
+    /** "4": <code>op</code>; implies <code>max_locals &gt;= 5</code> */
+    public static final int FMT_NO_ARGS_LOCALS_5 = 6;
+
+    /** "b": <code>op target target</code> */
+    public static final int FMT_BRANCH = 7;
+
+    /** "c": <code>op target target target target</code> */
+    public static final int FMT_WIDE_BRANCH = 8;
+
+    /** "p": <code>op #cpi #cpi</code>; constant restricted as specified */
+    public static final int FMT_CPI = 9;
+
+    /**
+     * "l": <code>op local</code>; category-1 local; implies
+     * <code>max_locals</code> is at least two more than the given
+     * local number 
+     */
+    public static final int FMT_LOCAL_1 = 10;
+
+    /**
+     * "m": <code>op local</code>; category-2 local; implies
+     * <code>max_locals</code> is at least two more than the given
+     * local number 
+     */
+    public static final int FMT_LOCAL_2 = 11;
+
+    /**
+     * "y": <code>op #byte</code> (<code>bipush</code> and
+     * <code>newarray</code>) 
+     */
+    public static final int FMT_LITERAL_BYTE = 12;
+
+    /** "I": <code>invokeinterface cpi cpi count 0</code> */
+    public static final int FMT_INVOKEINTERFACE = 13;
+
+    /** "L": <code>ldc #cpi</code>; constant restricted as specified */
+    public static final int FMT_LDC = 14;
+
+    /** "S": <code>sipush #byte #byte</code> */
+    public static final int FMT_SIPUSH = 15;
+
+    /** "T": <code>tableswitch ...</code> */
+    public static final int FMT_TABLESWITCH = 16;
+
+    /** "U": <code>lookupswitch ...</code> */
+    public static final int FMT_LOOKUPSWITCH = 17;
+
+    /** "M": <code>multianewarray cpi cpi dims</code> */
+    public static final int FMT_MULTIANEWARRAY = 18;
+
+    /** "W": <code>wide ...</code> */
+    public static final int FMT_WIDE = 19;
+
+    /** mask for the bits representing the opcode format */
+    public static final int FMT_MASK = 0x1f;
+
+    /** "I": flag bit for valid cp type for <code>Integer</code> */
+    public static final int CPOK_Integer = 0x20;
+
+    /** "F": flag bit for valid cp type for <code>Float</code> */
+    public static final int CPOK_Float = 0x40;
+
+    /** "J": flag bit for valid cp type for <code>Long</code> */
+    public static final int CPOK_Long = 0x80;
+
+    /** "D": flag bit for valid cp type for <code>Double</code> */
+    public static final int CPOK_Double = 0x100;
+
+    /** "c": flag bit for valid cp type for <code>Class</code> */
+    public static final int CPOK_Class = 0x200;
+
+    /** "s": flag bit for valid cp type for <code>String</code> */
+    public static final int CPOK_String = 0x400;
+
+    /** "f": flag bit for valid cp type for <code>Fieldref</code> */
+    public static final int CPOK_Fieldref = 0x800;
+
+    /** "m": flag bit for valid cp type for <code>Methodref</code> */
+    public static final int CPOK_Methodref = 0x1000;
+
+    /** "i": flag bit for valid cp type for <code>InterfaceMethodref</code> */
+    public static final int CPOK_InterfaceMethodref = 0x2000;
+
+    /**
+     * non-null; map from opcodes to format or'ed with allowed constant
+     * pool types 
+     */
+    private static final int[] OPCODE_INFO = new int[256];
+
+    /** non-null; map from opcodes to their names */
+    private static final String[] OPCODE_NAMES = new String[256];
+
+    /** non-null; bigass string describing all the opcodes */
+    private static final String OPCODE_DETAILS =
+        "00 - nop;" +
+        "01 - aconst_null;" +
+        "02 - iconst_m1;" +
+        "03 - iconst_0;" +
+        "04 - iconst_1;" +
+        "05 - iconst_2;" +
+        "06 - iconst_3;" +
+        "07 - iconst_4;" +
+        "08 - iconst_5;" +
+        "09 - lconst_0;" +
+        "0a - lconst_1;" +
+        "0b - fconst_0;" +
+        "0c - fconst_1;" +
+        "0d - fconst_2;" +
+        "0e - dconst_0;" +
+        "0f - dconst_1;" +
+        "10 y bipush;" +
+        "11 S sipush;" +
+        "12 L:IFcs ldc;" +
+        "13 p:IFcs ldc_w;" +
+        "14 p:DJ ldc2_w;" +
+        "15 l iload;" +
+        "16 m lload;" +
+        "17 l fload;" +
+        "18 m dload;" +
+        "19 l aload;" +
+        "1a 0 iload_0;" +
+        "1b 1 iload_1;" +
+        "1c 2 iload_2;" +
+        "1d 3 iload_3;" +
+        "1e 1 lload_0;" +
+        "1f 2 lload_1;" +
+        "20 3 lload_2;" +
+        "21 4 lload_3;" +
+        "22 0 fload_0;" +
+        "23 1 fload_1;" +
+        "24 2 fload_2;" +
+        "25 3 fload_3;" +
+        "26 1 dload_0;" +
+        "27 2 dload_1;" +
+        "28 3 dload_2;" +
+        "29 4 dload_3;" +
+        "2a 0 aload_0;" +
+        "2b 1 aload_1;" +
+        "2c 2 aload_2;" +
+        "2d 3 aload_3;" +
+        "2e - iaload;" +
+        "2f - laload;" +
+        "30 - faload;" +
+        "31 - daload;" +
+        "32 - aaload;" +
+        "33 - baload;" +
+        "34 - caload;" +
+        "35 - saload;" +
+        "36 - istore;" +
+        "37 - lstore;" +
+        "38 - fstore;" +
+        "39 - dstore;" +
+        "3a - astore;" +
+        "3b 0 istore_0;" +
+        "3c 1 istore_1;" +
+        "3d 2 istore_2;" +
+        "3e 3 istore_3;" +
+        "3f 1 lstore_0;" +
+        "40 2 lstore_1;" +
+        "41 3 lstore_2;" +
+        "42 4 lstore_3;" +
+        "43 0 fstore_0;" +
+        "44 1 fstore_1;" +
+        "45 2 fstore_2;" +
+        "46 3 fstore_3;" +
+        "47 1 dstore_0;" +
+        "48 2 dstore_1;" +
+        "49 3 dstore_2;" +
+        "4a 4 dstore_3;" +
+        "4b 0 astore_0;" +
+        "4c 1 astore_1;" +
+        "4d 2 astore_2;" +
+        "4e 3 astore_3;" +
+        "4f - iastore;" +
+        "50 - lastore;" +
+        "51 - fastore;" +
+        "52 - dastore;" +
+        "53 - aastore;" +
+        "54 - bastore;" +
+        "55 - castore;" +
+        "56 - sastore;" +
+        "57 - pop;" +
+        "58 - pop2;" +
+        "59 - dup;" +
+        "5a - dup_x1;" +
+        "5b - dup_x2;" +
+        "5c - dup2;" +
+        "5d - dup2_x1;" +
+        "5e - dup2_x2;" +
+        "5f - swap;" +
+        "60 - iadd;" +
+        "61 - ladd;" +
+        "62 - fadd;" +
+        "63 - dadd;" +
+        "64 - isub;" +
+        "65 - lsub;" +
+        "66 - fsub;" +
+        "67 - dsub;" +
+        "68 - imul;" +
+        "69 - lmul;" +
+        "6a - fmul;" +
+        "6b - dmul;" +
+        "6c - idiv;" +
+        "6d - ldiv;" +
+        "6e - fdiv;" +
+        "6f - ddiv;" +
+        "70 - irem;" +
+        "71 - lrem;" +
+        "72 - frem;" +
+        "73 - drem;" +
+        "74 - ineg;" +
+        "75 - lneg;" +
+        "76 - fneg;" +
+        "77 - dneg;" +
+        "78 - ishl;" +
+        "79 - lshl;" +
+        "7a - ishr;" +
+        "7b - lshr;" +
+        "7c - iushr;" +
+        "7d - lushr;" +
+        "7e - iand;" +
+        "7f - land;" +
+        "80 - ior;" +
+        "81 - lor;" +
+        "82 - ixor;" +
+        "83 - lxor;" +
+        "84 l iinc;" +
+        "85 - i2l;" +
+        "86 - i2f;" +
+        "87 - i2d;" +
+        "88 - l2i;" +
+        "89 - l2f;" +
+        "8a - l2d;" +
+        "8b - f2i;" +
+        "8c - f2l;" +
+        "8d - f2d;" +
+        "8e - d2i;" +
+        "8f - d2l;" +
+        "90 - d2f;" +
+        "91 - i2b;" +
+        "92 - i2c;" +
+        "93 - i2s;" +
+        "94 - lcmp;" +
+        "95 - fcmpl;" +
+        "96 - fcmpg;" +
+        "97 - dcmpl;" +
+        "98 - dcmpg;" +
+        "99 b ifeq;" +
+        "9a b ifne;" +
+        "9b b iflt;" +
+        "9c b ifge;" +
+        "9d b ifgt;" +
+        "9e b ifle;" +
+        "9f b if_icmpeq;" +
+        "a0 b if_icmpne;" +
+        "a1 b if_icmplt;" +
+        "a2 b if_icmpge;" +
+        "a3 b if_icmpgt;" +
+        "a4 b if_icmple;" +
+        "a5 b if_acmpeq;" +
+        "a6 b if_acmpne;" +
+        "a7 b goto;" +
+        "a8 b jsr;" +
+        "a9 l ret;" +
+        "aa T tableswitch;" +
+        "ab U lookupswitch;" +
+        "ac - ireturn;" +
+        "ad - lreturn;" +
+        "ae - freturn;" +
+        "af - dreturn;" +
+        "b0 - areturn;" +
+        "b1 - return;" +
+        "b2 p:f getstatic;" +
+        "b3 p:f putstatic;" +
+        "b4 p:f getfield;" +
+        "b5 p:f putfield;" +
+        "b6 p:m invokevirtual;" +
+        "b7 p:m invokespecial;" +
+        "b8 p:m invokestatic;" +
+        "b9 I:i invokeinterface;" +
+        "bb p:c new;" +
+        "bc y newarray;" +
+        "bd p:c anewarray;" +
+        "be - arraylength;" +
+        "bf - athrow;" +
+        "c0 p:c checkcast;" +
+        "c1 p:c instanceof;" +
+        "c2 - monitorenter;" +
+        "c3 - monitorexit;" +
+        "c4 W wide;" +
+        "c5 M:c multianewarray;" +
+        "c6 b ifnull;" +
+        "c7 b ifnonnull;" +
+        "c8 c goto_w;" +
+        "c9 c jsr_w;";
+
+    static {
+        // Set up OPCODE_INFO and OPCODE_NAMES.
+        String s = OPCODE_DETAILS;
+        int len = s.length();
+
+        for (int i = 0; i < len; /*i*/) {
+            int idx = (Character.digit(s.charAt(i), 16) << 4) |
+                Character.digit(s.charAt(i + 1), 16);
+            int info;
+            switch (s.charAt(i + 3)) {
+                case '-': info = FMT_NO_ARGS; break;
+                case '0': info = FMT_NO_ARGS_LOCALS_1; break;
+                case '1': info = FMT_NO_ARGS_LOCALS_2; break;
+                case '2': info = FMT_NO_ARGS_LOCALS_3; break;
+                case '3': info = FMT_NO_ARGS_LOCALS_4; break;
+                case '4': info = FMT_NO_ARGS_LOCALS_5; break;
+                case 'b': info = FMT_BRANCH; break;
+                case 'c': info = FMT_WIDE_BRANCH; break;
+                case 'p': info = FMT_CPI; break;
+                case 'l': info = FMT_LOCAL_1; break;
+                case 'm': info = FMT_LOCAL_2; break;
+                case 'y': info = FMT_LITERAL_BYTE; break;
+                case 'I': info = FMT_INVOKEINTERFACE; break;
+                case 'L': info = FMT_LDC; break;
+                case 'S': info = FMT_SIPUSH; break;
+                case 'T': info = FMT_TABLESWITCH; break;
+                case 'U': info = FMT_LOOKUPSWITCH; break;
+                case 'M': info = FMT_MULTIANEWARRAY; break;
+                case 'W': info = FMT_WIDE; break;
+                default: info = FMT_INVALID; break;
+            }
+
+            i += 5;
+            if (s.charAt(i - 1) == ':') {
+                inner:
+                for (;;) {
+                    switch (s.charAt(i)) {
+                        case 'I': info |= CPOK_Integer; break;
+                        case 'F': info |= CPOK_Float; break;
+                        case 'J': info |= CPOK_Long; break;
+                        case 'D': info |= CPOK_Double; break;
+                        case 'c': info |= CPOK_Class; break;
+                        case 's': info |= CPOK_String; break;
+                        case 'f': info |= CPOK_Fieldref; break;
+                        case 'm': info |= CPOK_Methodref; break;
+                        case 'i': info |= CPOK_InterfaceMethodref; break;
+                        default: break inner;
+                    }
+                    i++;
+                }
+                i++;
+            }
+
+            int endAt = s.indexOf(';', i);
+            OPCODE_INFO[idx] = info;
+            OPCODE_NAMES[idx] = s.substring(i, endAt);
+            i = endAt + 1;
+        }
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private ByteOps() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the name of the given opcode.
+     * 
+     * @param opcode &gt;= 0, &lt;= 255; the opcode
+     * @return non-null; its name
+     */
+    public static String opName(int opcode) {
+        String result = OPCODE_NAMES[opcode];
+
+        if (result == null) {
+            result = "unused_" + Hex.u1(opcode);
+            OPCODE_NAMES[opcode] = result;
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the format and allowed cp types of the given opcode.
+     * 
+     * @param opcode &gt;= 0, &lt;= 255; the opcode
+     * @return its format and allowed cp types
+     */
+    public static int opInfo(int opcode) {
+        return OPCODE_INFO[opcode];
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/BytecodeArray.java b/dx/src/com/android/dx/cf/code/BytecodeArray.java
new file mode 100644
index 0000000..71ba029
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java
@@ -0,0 +1,1393 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import java.util.ArrayList;
+
+/**
+ * Bytecode array, which is part of a standard <code>Code</code> attribute.
+ */
+public final class BytecodeArray {
+    /** convenient no-op implementation of {@link Visitor} */
+    public static final Visitor EMPTY_VISITOR = new BaseVisitor();
+
+    /** non-null; underlying bytes */
+    private final ByteArray bytes;
+
+    /** non-null; constant pool to use when resolving constant pool indices */
+    private final ConstantPool pool;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; underlying bytes
+     * @param pool non-null; constant pool to use when resolving constant
+     * pool indices
+     */
+    public BytecodeArray(ByteArray bytes, ConstantPool pool) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (pool == null) {
+            throw new NullPointerException("pool == null");
+        }
+
+        this.bytes = bytes;
+        this.pool = pool;
+    }
+
+    /**
+     * Gets the underlying byte array.
+     * 
+     * @return non-null; the byte array
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the size of the bytecode array, per se.
+     * 
+     * @return &gt;= 0; the length of the bytecode array
+     */
+    public int size() {
+        return bytes.size();
+    }
+
+    /**
+     * Gets the total length of this structure in bytes, when included in
+     * a <code>Code</code> attribute. The returned value includes the
+     * array size plus four bytes for <code>code_length</code>.
+     * 
+     * @return &gt;= 4; the total length, in bytes
+     */
+    public int byteLength() {
+        return 4 + bytes.size();
+    }
+
+    /**
+     * Parses each instruction in the array, in order.
+     * 
+     * @param visitor null-ok; visitor to call back to for each instruction
+     */
+    public void forEach(Visitor visitor) {
+        int sz = bytes.size();
+        int at = 0;
+
+        while (at < sz) {
+            /*
+             * Don't record the previous offset here, so that we get to see the
+             * raw code that initializes the array
+             */
+            at += parseInstruction(at, visitor);
+        }
+    }
+
+    /**
+     * Finds the offset to each instruction in the bytecode array. The
+     * result is a bit set with the offset of each opcode-per-se flipped on.
+     * 
+     * @see Bits
+     * @return non-null; appropriately constructed bit set
+     */
+    public int[] getInstructionOffsets() {
+        int sz = bytes.size();
+        int[] result = Bits.makeBitSet(sz);
+        int at = 0;
+
+        while (at < sz) {
+            Bits.set(result, at, true);
+            int length = parseInstruction(at, null);
+            at += length;
+        }
+
+        return result;
+    }
+
+    /**
+     * Processes the given "work set" by repeatedly finding the lowest bit
+     * in the set, clearing it, and parsing and visiting the instruction at
+     * the indicated offset (that is, the bit index), repeating until the
+     * work set is empty. It is expected that the visitor will regularly
+     * set new bits in the work set during the process.
+     * 
+     * @param workSet non-null; the work set to process
+     * @param visitor non-null; visitor to call back to for each instruction
+     */
+    public void processWorkSet(int[] workSet, Visitor visitor) {
+        if (visitor == null) {
+            throw new NullPointerException("visitor == null");
+        }
+
+        for (;;) {
+            int offset = Bits.findFirst(workSet, 0);
+            if (offset < 0) {
+                break;
+            }
+            Bits.clear(workSet, offset);
+            parseInstruction(offset, visitor);
+            visitor.setPreviousOffset(offset);
+        }
+    }
+
+    /**
+     * Parses the instruction at the indicated offset. Indicate the
+     * result by calling the visitor if supplied and by returning the
+     * number of bytes consumed by the instruction.
+     * 
+     * <p>In order to simplify further processing, the opcodes passed
+     * to the visitor are canonicalized, altering the opcode to a more
+     * universal one and making formerly implicit arguments
+     * explicit. In particular:</p>
+     * 
+     * <ul>
+     * <li>The opcodes to push literal constants of primitive types all become
+     *   <code>ldc</code>.
+     *   E.g., <code>fconst_0</code>, <code>sipush</code>, and 
+     *   <code>lconst_0</code> qualify for this treatment.</li>
+     * <li><code>aconst_null</code> becomes <code>ldc</code> of a
+     *   "known null."</li>
+     * <li>Shorthand local variable accessors become the corresponding
+     *   longhand. E.g. <code>aload_2</code> becomes <code>aload</code>.</li>
+     * <li><code>goto_w</code> and <code>jsr_w</code> become <code>goto</code>
+     *   and <code>jsr</code> (respectively).</li>
+     * <li><code>ldc_w</code> becomes <code>ldc</code>.</li>
+     * <li><code>tableswitch</code> becomes <code>lookupswitch</code>.
+     * <li>Arithmetic, array, and value-returning ops are collapsed
+     *   to the <code>int</code> variant opcode, with the <code>type</code>
+     *   argument set to indicate the actual type. E.g.,
+     *   <code>fadd</code> becomes <code>iadd</code>, but
+     *   <code>type</code> is passed as <code>Type.FLOAT</code> in that
+     *   case. Similarly, <code>areturn</code> becomes
+     *   <code>ireturn</code>. (However, <code>return</code> remains
+     *   unchanged.</li>
+     * <li>Local variable access ops are collapsed to the <code>int</code>
+     *   variant opcode, with the <code>type</code> argument set to indicate
+     *   the actual type. E.g., <code>aload</code> becomes <code>iload</code>,
+     *   but <code>type</code> is passed as <code>Type.OBJECT</code> in
+     *   that case.</li>
+     * <li>Numeric conversion ops (<code>i2l</code>, etc.) are left alone
+     *   to avoid too much confustion, but their <code>type</code> is
+     *   the pushed type. E.g., <code>i2b</code> gets type
+     *   <code>Type.INT</code>, and <code>f2d</code> gets type
+     *   <code>Type.DOUBLE</code>. Other unaltered opcodes also get
+     *   their pushed type. E.g., <code>arraylength</code> gets type
+     *   <code>Type.INT</code>.</li>
+     * </ul>
+     * 
+     * @param offset &gt;= 0, &lt; bytes.size(); offset to the start of the
+     * instruction
+     * @param visitor null-ok; visitor to call back to
+     * @return the length of the instruction, in bytes
+     */
+    public int parseInstruction(int offset, Visitor visitor) {
+        if (visitor == null) {
+            visitor = EMPTY_VISITOR;
+        }
+
+        try {
+            int opcode = bytes.getUnsignedByte(offset);
+            int info = ByteOps.opInfo(opcode);
+            int fmt = info & ByteOps.FMT_MASK;
+
+            switch (opcode) {
+                case ByteOps.NOP: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.ACONST_NULL: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstKnownNull.THE_ONE, 0);
+                    return 1;
+                }
+                case ByteOps.ICONST_M1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_M1, -1);
+                    return 1;
+                }
+                case ByteOps.ICONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.ICONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_1, 1);
+                    return 1;
+                }
+                case ByteOps.ICONST_2: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_2, 2);
+                    return 1;
+                }
+                case ByteOps.ICONST_3: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_3, 3);
+                    return 1;
+                }
+                case ByteOps.ICONST_4: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_4, 4);
+                    return 1;
+                }
+                case ByteOps.ICONST_5:  {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_5, 5);
+                    return 1;
+                }
+                case ByteOps.LCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstLong.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.LCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstLong.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_2:  {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_2, 0);
+                    return 1;
+                }
+                case ByteOps.DCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstDouble.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.DCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstDouble.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.BIPUSH: {
+                    int value = bytes.getByte(offset + 1);
+                    visitor.visitConstant(ByteOps.LDC, offset, 2,
+                                          CstInteger.make(value), value);
+                    return 2;
+                }
+                case ByteOps.SIPUSH: {
+                    int value = bytes.getShort(offset + 1);
+                    visitor.visitConstant(ByteOps.LDC, offset, 3,
+                                          CstInteger.make(value), value);
+                    return 3;
+                }
+                case ByteOps.LDC: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    Constant cst = pool.get(idx);
+                    int value = (cst instanceof CstInteger) ? 
+                        ((CstInteger) cst).getValue() : 0;
+                    visitor.visitConstant(ByteOps.LDC, offset, 2, cst, value);
+                    return 2;
+                }
+                case ByteOps.LDC_W: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    int value = (cst instanceof CstInteger) ? 
+                        ((CstInteger) cst).getValue() : 0;
+                    visitor.visitConstant(ByteOps.LDC, offset, 3, cst, value);
+                    return 3;
+                }
+                case ByteOps.LDC2_W: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(ByteOps.LDC2_W, offset, 3, cst, 0);
+                    return 3;
+                }
+                case ByteOps.ILOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.INT, 0);
+                    return 2;
+                }
+                case ByteOps.LLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.LONG, 0);
+                    return 2;
+                }
+                case ByteOps.FLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.FLOAT, 0);
+                    return 2;
+                }
+                case ByteOps.DLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.DOUBLE, 0);
+                    return 2;
+                }
+                case ByteOps.ALOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.OBJECT, 0);
+                    return 2;
+                }
+                case ByteOps.ILOAD_0:
+                case ByteOps.ILOAD_1:
+                case ByteOps.ILOAD_2:
+                case ByteOps.ILOAD_3: {
+                    int idx = opcode - ByteOps.ILOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.INT, 0);
+                    return 1;
+                }
+                case ByteOps.LLOAD_0:
+                case ByteOps.LLOAD_1:
+                case ByteOps.LLOAD_2:
+                case ByteOps.LLOAD_3: {
+                    int idx = opcode - ByteOps.LLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.LONG, 0);
+                    return 1;
+                }
+                case ByteOps.FLOAD_0:
+                case ByteOps.FLOAD_1:
+                case ByteOps.FLOAD_2:
+                case ByteOps.FLOAD_3: {
+                    int idx = opcode - ByteOps.FLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.FLOAT, 0);
+                    return 1;
+                }
+                case ByteOps.DLOAD_0:
+                case ByteOps.DLOAD_1:
+                case ByteOps.DLOAD_2:
+                case ByteOps.DLOAD_3: {
+                    int idx = opcode - ByteOps.DLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.DOUBLE, 0);
+                    return 1;
+                }
+                case ByteOps.ALOAD_0:
+                case ByteOps.ALOAD_1:
+                case ByteOps.ALOAD_2:
+                case ByteOps.ALOAD_3: {
+                    int idx = opcode - ByteOps.ALOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.OBJECT, 0);
+                    return 1;
+                }
+                case ByteOps.IALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.AALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.BALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.BYTE);
+                    return 1;
+                }
+                case ByteOps.CALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.CHAR);
+                    return 1;
+                }
+                case ByteOps.SALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.SHORT);
+                    return 1;
+                }
+                case ByteOps.ISTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.INT, 0);
+                    return 2;
+                }
+                case ByteOps.LSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.LONG, 0);
+                    return 2;
+                }
+                case ByteOps.FSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.FLOAT, 0);
+                    return 2;
+                }
+                case ByteOps.DSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.DOUBLE, 0);
+                    return 2;
+                }
+                case ByteOps.ASTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.OBJECT, 0);
+                    return 2;
+                }
+                case ByteOps.ISTORE_0:
+                case ByteOps.ISTORE_1:
+                case ByteOps.ISTORE_2:
+                case ByteOps.ISTORE_3: {
+                    int idx = opcode - ByteOps.ISTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.INT, 0);
+                    return 1;
+                }
+                case ByteOps.LSTORE_0:
+                case ByteOps.LSTORE_1:
+                case ByteOps.LSTORE_2:
+                case ByteOps.LSTORE_3: {
+                    int idx = opcode - ByteOps.LSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.LONG, 0);
+                    return 1;
+                }
+                case ByteOps.FSTORE_0:
+                case ByteOps.FSTORE_1:
+                case ByteOps.FSTORE_2:
+                case ByteOps.FSTORE_3: {
+                    int idx = opcode - ByteOps.FSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.FLOAT, 0);
+                    return 1;
+                }
+                case ByteOps.DSTORE_0:
+                case ByteOps.DSTORE_1:
+                case ByteOps.DSTORE_2:
+                case ByteOps.DSTORE_3: {
+                    int idx = opcode - ByteOps.DSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.DOUBLE, 0);
+                    return 1;
+                }
+                case ByteOps.ASTORE_0:
+                case ByteOps.ASTORE_1:
+                case ByteOps.ASTORE_2:
+                case ByteOps.ASTORE_3: {
+                    int idx = opcode - ByteOps.ASTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.OBJECT, 0);
+                    return 1;
+                }
+                case ByteOps.IASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.AASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.BASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.BYTE);
+                    return 1;
+                }
+                case ByteOps.CASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.CHAR);
+                    return 1;
+                }
+                case ByteOps.SASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.SHORT);
+                    return 1;
+                }
+                case ByteOps.POP:
+                case ByteOps.POP2:
+                case ByteOps.DUP:
+                case ByteOps.DUP_X1:
+                case ByteOps.DUP_X2:
+                case ByteOps.DUP2:
+                case ByteOps.DUP2_X1:
+                case ByteOps.DUP2_X2:
+                case ByteOps.SWAP: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.IADD:
+                case ByteOps.ISUB:
+                case ByteOps.IMUL:
+                case ByteOps.IDIV:
+                case ByteOps.IREM:
+                case ByteOps.INEG:
+                case ByteOps.ISHL:
+                case ByteOps.ISHR:
+                case ByteOps.IUSHR:
+                case ByteOps.IAND:
+                case ByteOps.IOR:
+                case ByteOps.IXOR: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LADD:
+                case ByteOps.LSUB:
+                case ByteOps.LMUL:
+                case ByteOps.LDIV:
+                case ByteOps.LREM:
+                case ByteOps.LNEG:
+                case ByteOps.LSHL:
+                case ByteOps.LSHR:
+                case ByteOps.LUSHR:
+                case ByteOps.LAND:
+                case ByteOps.LOR:
+                case ByteOps.LXOR: {
+                    /*
+                     * It's "opcode - 1" because, conveniently enough, all
+                     * these long ops are one past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 1, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FADD:
+                case ByteOps.FSUB:
+                case ByteOps.FMUL:
+                case ByteOps.FDIV:
+                case ByteOps.FREM:
+                case ByteOps.FNEG: {
+                    /*
+                     * It's "opcode - 2" because, conveniently enough, all
+                     * these float ops are two past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 2, offset, 1, Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DADD:
+                case ByteOps.DSUB:
+                case ByteOps.DMUL:
+                case ByteOps.DDIV:
+                case ByteOps.DREM:
+                case ByteOps.DNEG: {
+                    /*
+                     * It's "opcode - 3" because, conveniently enough, all
+                     * these double ops are three past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 3, offset, 1, Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.IINC: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    int value = bytes.getByte(offset + 2);
+                    visitor.visitLocal(opcode, offset, 3, idx,
+                                       Type.INT, value);
+                    return 3;
+                }
+                case ByteOps.I2L:
+                case ByteOps.F2L:
+                case ByteOps.D2L: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.I2F:
+                case ByteOps.L2F:
+                case ByteOps.D2F: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.I2D:
+                case ByteOps.L2D:
+                case ByteOps.F2D: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.L2I:
+                case ByteOps.F2I:
+                case ByteOps.D2I:
+                case ByteOps.I2B:
+                case ByteOps.I2C:
+                case ByteOps.I2S:
+                case ByteOps.LCMP:
+                case ByteOps.FCMPL:
+                case ByteOps.FCMPG:
+                case ByteOps.DCMPL:
+                case ByteOps.DCMPG:
+                case ByteOps.ARRAYLENGTH: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.IFEQ:
+                case ByteOps.IFNE:
+                case ByteOps.IFLT:
+                case ByteOps.IFGE:
+                case ByteOps.IFGT:
+                case ByteOps.IFLE:
+                case ByteOps.IF_ICMPEQ:
+                case ByteOps.IF_ICMPNE:
+                case ByteOps.IF_ICMPLT:
+                case ByteOps.IF_ICMPGE:
+                case ByteOps.IF_ICMPGT:
+                case ByteOps.IF_ICMPLE:
+                case ByteOps.IF_ACMPEQ:
+                case ByteOps.IF_ACMPNE:
+                case ByteOps.GOTO:
+                case ByteOps.JSR:
+                case ByteOps.IFNULL:
+                case ByteOps.IFNONNULL: {
+                    int target = offset + bytes.getShort(offset + 1);
+                    visitor.visitBranch(opcode, offset, 3, target);
+                    return 3;
+                }
+                case ByteOps.RET: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(opcode, offset, 2, idx,
+                                       Type.RETURN_ADDRESS, 0);
+                    return 2;
+                }
+                case ByteOps.TABLESWITCH: {
+                    return parseTableswitch(offset, visitor);
+                }
+                case ByteOps.LOOKUPSWITCH: {
+                    return parseLookupswitch(offset, visitor);
+                }
+                case ByteOps.IRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.ARETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.RETURN:
+                case ByteOps.ATHROW:
+                case ByteOps.MONITORENTER:
+                case ByteOps.MONITOREXIT: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.GETSTATIC:
+                case ByteOps.PUTSTATIC:
+                case ByteOps.GETFIELD:
+                case ByteOps.PUTFIELD:
+                case ByteOps.INVOKEVIRTUAL:
+                case ByteOps.INVOKESPECIAL:
+                case ByteOps.INVOKESTATIC:
+                case ByteOps.NEW:
+                case ByteOps.ANEWARRAY:
+                case ByteOps.CHECKCAST:
+                case ByteOps.INSTANCEOF: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 3, cst, 0);
+                    return 3;
+                }
+                case ByteOps.INVOKEINTERFACE: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    int count = bytes.getUnsignedByte(offset + 3);
+                    int expectZero = bytes.getUnsignedByte(offset + 4);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 5, cst,
+                                          count | (expectZero << 8));
+                    return 5;
+                }
+                case ByteOps.NEWARRAY: {
+                    return parseNewarray(offset, visitor);
+                }
+                case ByteOps.WIDE: {
+                    return parseWide(offset, visitor);
+                }
+                case ByteOps.MULTIANEWARRAY: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    int dimensions = bytes.getUnsignedByte(offset + 3);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 4, cst, dimensions);
+                    return 4;
+                }
+                case ByteOps.GOTO_W:
+                case ByteOps.JSR_W: {
+                    int target = offset + bytes.getInt(offset + 1);
+                    int newop =
+                        (opcode == ByteOps.GOTO_W) ? ByteOps.GOTO :
+                        ByteOps.JSR;
+                    visitor.visitBranch(newop, offset, 5, target);
+                    return 5;
+                }
+                default: {
+                    visitor.visitInvalid(opcode, offset, 1);
+                    return 1;
+                }
+            }
+        } catch (SimException ex) {
+            ex.addContext("...at bytecode offset " + Hex.u4(offset));
+            throw ex;
+        } catch (RuntimeException ex) {
+            SimException se = new SimException(ex);
+            se.addContext("...at bytecode offset " + Hex.u4(offset));
+            throw se;
+        }
+    }
+
+    /**
+     * Helper to deal with <code>tableswitch</code>.
+     * 
+     * @param offset the offset to the <code>tableswitch</code> opcode itself
+     * @param visitor non-null; visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseTableswitch(int offset, Visitor visitor) {
+        int at = (offset + 4) & ~3; // "at" skips the padding.
+
+        // Collect the padding.
+        int padding = 0;
+        for (int i = offset + 1; i < at; i++) {
+            padding = (padding << 8) | bytes.getUnsignedByte(i);
+        }
+
+        int defaultTarget = offset + bytes.getInt(at);
+        int low = bytes.getInt(at + 4);
+        int high = bytes.getInt(at + 8);
+        int count = high - low + 1;
+        at += 12;
+
+        if (low > high) {
+            throw new SimException("low / high inversion");
+        }
+
+        SwitchList cases = new SwitchList(count);
+        for (int i = 0; i < count; i++) {
+            int target = offset + bytes.getInt(at);
+            at += 4;
+            cases.add(low + i, target);
+        }
+        cases.setDefaultTarget(defaultTarget);
+        cases.removeSuperfluousDefaults();
+        cases.setImmutable();
+
+        int length = at - offset;
+        visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+                            padding);
+
+        return length;
+    }
+
+    /**
+     * Helper to deal with <code>lookupswitch</code>.
+     * 
+     * @param offset the offset to the <code>lookupswitch</code> opcode itself
+     * @param visitor non-null; visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseLookupswitch(int offset, Visitor visitor) {
+        int at = (offset + 4) & ~3; // "at" skips the padding.
+
+        // Collect the padding.
+        int padding = 0;
+        for (int i = offset + 1; i < at; i++) {
+            padding = (padding << 8) | bytes.getUnsignedByte(i);
+        }
+
+        int defaultTarget = offset + bytes.getInt(at);
+        int npairs = bytes.getInt(at + 4);
+        at += 8;
+
+        SwitchList cases = new SwitchList(npairs);
+        for (int i = 0; i < npairs; i++) {
+            int match = bytes.getInt(at);
+            int target = offset + bytes.getInt(at + 4);
+            at += 8;
+            cases.add(match, target);
+        }
+        cases.setDefaultTarget(defaultTarget);
+        cases.removeSuperfluousDefaults();
+        cases.setImmutable();
+
+        int length = at - offset;
+        visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+                            padding);
+
+        return length;
+    }
+
+    /**
+     * Helper to deal with <code>newarray</code>.
+     *
+     * @param offset the offset to the <code>newarray</code> opcode itself
+     * @param visitor non-null; visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseNewarray(int offset, Visitor visitor) {
+        int value = bytes.getUnsignedByte(offset + 1);
+        CstType type;
+        switch (value) {
+            case ByteOps.NEWARRAY_BOOLEAN: {
+                type = CstType.BOOLEAN_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_CHAR: {
+                type = CstType.CHAR_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_DOUBLE: {
+                type = CstType.DOUBLE_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_FLOAT: {
+                type = CstType.FLOAT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_BYTE: {
+                type = CstType.BYTE_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_SHORT: {
+                type = CstType.SHORT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_INT: {
+                type = CstType.INT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_LONG: {
+                type = CstType.LONG_ARRAY;
+                break;
+            }
+            default: {
+                throw new SimException("bad newarray code " +
+                        Hex.u1(value));
+            }
+        }
+
+        // Revisit the previous bytecode to find out the length of the array
+        int previousOffset = visitor.getPreviousOffset();
+        ConstantParserVisitor constantVisitor = new ConstantParserVisitor();
+        int arrayLength = 0;
+
+        /*
+         * For visitors that don't record the previous offset, -1 will be
+         * seen here
+         */
+        if (previousOffset >= 0) {
+            parseInstruction(previousOffset, constantVisitor);
+            if (constantVisitor.cst instanceof CstInteger &&
+                    constantVisitor.length + previousOffset == offset) {
+                arrayLength = constantVisitor.value;
+
+            }
+        }
+
+        /*
+         * Try to match the array initialization idiom. For example, if the
+         * subsequent code is initializing an int array, we are expecting the
+         * following pattern repeatedly:
+         *  dup
+         *  push index
+         *  push value
+         *  *astore
+         *
+         * where the index value will be incrimented sequentially from 0 up.
+         */
+        int nInit = 0;
+        int curOffset = offset+2;
+        int lastOffset = curOffset;
+        ArrayList<Constant> initVals = new ArrayList<Constant>();
+
+        if (arrayLength != 0) {
+            while (true) {
+                boolean punt = false;
+
+                // First check if the next bytecode is dup
+                int nextByte = bytes.getUnsignedByte(curOffset++);
+                if (nextByte != ByteOps.DUP)
+                    break;
+
+                // Next check if the expected array index is pushed to the stack
+                parseInstruction(curOffset, constantVisitor);
+                if (constantVisitor.length == 0 ||
+                        !(constantVisitor.cst instanceof CstInteger) ||
+                        constantVisitor.value != nInit)
+                    break;
+
+                // Next, fetch the init value and record it
+                curOffset += constantVisitor.length;
+
+                // Next find out what kind of constant is pushed onto the stack
+                parseInstruction(curOffset, constantVisitor);
+                if (constantVisitor.length == 0 ||
+                        !(constantVisitor.cst instanceof CstLiteralBits))
+                    break;
+
+                curOffset += constantVisitor.length;
+                initVals.add(constantVisitor.cst);
+
+                nextByte = bytes.getUnsignedByte(curOffset++);
+                // Now, check if the value is stored to the array properly
+                switch (value) {
+                    case ByteOps.NEWARRAY_BYTE:
+                    case ByteOps.NEWARRAY_BOOLEAN: {
+                        if (nextByte != ByteOps.BASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_CHAR: {
+                        if (nextByte != ByteOps.CASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_DOUBLE: {
+                        if (nextByte != ByteOps.DASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_FLOAT: {
+                        if (nextByte != ByteOps.FASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_SHORT: {
+                        if (nextByte != ByteOps.SASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_INT: {
+                        if (nextByte != ByteOps.IASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_LONG: {
+                        if (nextByte != ByteOps.LASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    default:
+                        punt = true;
+                        break;
+                }
+                if (punt) {
+                    break;
+                }
+                lastOffset = curOffset;
+                nInit++;
+            }
+        }
+
+        /*
+         * For singleton arrays it is still more economical to
+         * generate the aput.
+         */
+        if (nInit < 2 || nInit != arrayLength) {
+            visitor.visitNewarray(offset, 2, type, null);
+            return 2;
+        } else {
+            visitor.visitNewarray(offset, lastOffset - offset, type, initVals);
+            return lastOffset - offset;
+        }
+     }
+
+    
+    /**
+     * Helper to deal with <code>wide</code>.
+     * 
+     * @param offset the offset to the <code>wide</code> opcode itself
+     * @param visitor non-null; visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseWide(int offset, Visitor visitor) {
+        int opcode = bytes.getUnsignedByte(offset + 1);
+        int idx = bytes.getUnsignedShort(offset + 2);
+        switch (opcode) {
+            case ByteOps.ILOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.INT, 0);
+                return 4;
+            }
+            case ByteOps.LLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.LONG, 0);
+                return 4;
+            }
+            case ByteOps.FLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.FLOAT, 0);
+                return 4;
+            }
+            case ByteOps.DLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.DOUBLE, 0);
+                return 4;
+            }
+            case ByteOps.ALOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.OBJECT, 0);
+                return 4;
+            }
+            case ByteOps.ISTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.INT, 0);
+                return 4;
+            }
+            case ByteOps.LSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.LONG, 0);
+                return 4;
+            }
+            case ByteOps.FSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.FLOAT, 0);
+                return 4;
+            }
+            case ByteOps.DSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.DOUBLE, 0);
+                return 4;
+            }
+            case ByteOps.ASTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.OBJECT, 0);
+                return 4;
+            }
+            case ByteOps.RET: {
+                visitor.visitLocal(opcode, offset, 4, idx,
+                                   Type.RETURN_ADDRESS, 0);
+                return 4;
+            }
+            case ByteOps.IINC: {
+                int value = bytes.getShort(offset + 4);
+                visitor.visitLocal(opcode, offset, 6, idx,
+                                   Type.INT, value);
+                return 6;
+            }
+            default: {
+                visitor.visitInvalid(ByteOps.WIDE, offset, 1);
+                return 1;
+            }
+        }
+    }
+
+    /**
+     * Instruction visitor interface.
+     */
+    public interface Visitor {
+        /**
+         * Visits an invalid instruction.
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         */
+        public void visitInvalid(int opcode, int offset, int length);
+
+        /**
+         * Visits an instruction which has no inline arguments
+         * (implicit or explicit).
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param type non-null; type the instruction operates on
+         */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type);
+
+        /**
+         * Visits an instruction which has a local variable index argument.
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param idx the local variable index
+         * @param type non-null; the type of the accessed value
+         * @param value additional literal integer argument, if salient (i.e.,
+         * for <code>iinc</code>)
+         */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value);
+
+        /**
+         * Visits an instruction which has a (possibly synthetic)
+         * constant argument, and possibly also an
+         * additional literal integer argument. In the case of
+         * <code>multianewarray</code>, the argument is the count of
+         * dimensions. In the case of <code>invokeinterface</code>,
+         * the argument is the parameter count or'ed with the
+         * should-be-zero value left-shifted by 8. In the case of entries
+         * of type <code>int</code>, the <code>value</code> field always
+         * holds the raw value (for convenience of clients).
+         * 
+         * <p><b>Note:</b> In order to avoid giving it a barely-useful
+         * visitor all its own, <code>newarray</code> also uses this
+         * form, passing <code>value</code> as the array type code and
+         * <code>cst</code> as a {@link CstType} instance
+         * corresponding to the array type.</p>
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param cst non-null; the constant
+         * @param value additional literal integer argument, if salient
+         * (ignore if not)
+         */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value);
+
+        /**
+         * Visits an instruction which has a branch target argument.
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param target the absolute (not relative) branch target
+         */
+        public void visitBranch(int opcode, int offset, int length,
+                int target);
+
+        /**
+         * Visits a switch instruction.
+         * 
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param cases non-null; list of (value, target) pairs, plus the
+         * default target
+         * @param padding the bytes found in the padding area (if any),
+         * packed
+         */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding);
+
+        /**
+         * Visits a newarray instruction.
+         *
+         * @param offset   offset to the instruction
+         * @param length   length of the instruction, in bytes
+         * @param cst non-null; the type of the array
+         * @param initVals non-null; list of bytecode offsets for init values
+         */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initVals);
+
+        /**
+         * Set previous bytecode offset
+         * @param offset    offset of the previous fully parsed bytecode
+         */
+        public void setPreviousOffset(int offset);
+
+        /**
+         * Get previous bytecode offset
+         * @return return the recored offset of the previous bytecode
+         */
+        public int getPreviousOffset();
+    }
+
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    public static class BaseVisitor implements Visitor {
+
+        /** offset of the previously parsed bytecode */
+        private int previousOffset;
+
+        BaseVisitor() {
+            previousOffset = -1;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initValues) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            previousOffset = offset;
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            return previousOffset;
+        }
+    }
+    
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    class ConstantParserVisitor extends BaseVisitor {
+        Constant cst;
+        int length;
+        int value;
+
+        /** Empty constructor */
+        ConstantParserVisitor() {
+        }
+
+        private void clear() {
+            length = 0;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            this.cst = cst;
+            this.length = length;
+            this.value = value;
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initVals) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            // Intentionally left empty
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            // Intentionally left empty
+            return -1;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ConcreteMethod.java b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
new file mode 100644
index 0000000..47f698d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Container for all the giblets that make up a concrete Java bytecode method.
+ * It implements {@link Method}, so it provides all the original access
+ * (by delegation), but it also constructs and keeps useful versions of
+ * stuff extracted from the method's <code>Code</code> attribute.
+ */
+public final class ConcreteMethod implements Method {
+    /** non-null; method being wrapped */
+    private final Method method;
+
+    /**
+     * null-ok; the class's <code>SourceFile</code> attribute value,
+     * if any 
+     */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * whether the class that this method is part of is defined with
+     * <code>ACC_SUPER</code> 
+     */
+    private final boolean accSuper;
+
+    /** non-null; the code attribute */
+    private final AttCode attCode;
+
+    /** non-null; line number list */
+    private final LineNumberList lineNumbers;
+
+    /** non-null; local variable list */
+    private final LocalVariableList localVariables;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; the method to be based on
+     * @param cf non-null; the class file that contains this method
+     * @param keepLines whether to keep the line number information
+     * (if any)
+     * @param keepLocals whether to keep the local variable
+     * information (if any)
+     */
+    public ConcreteMethod(Method method, ClassFile cf, boolean keepLines,
+                          boolean keepLocals) {
+        this.method = method;
+        this.accSuper = (cf.getAccessFlags() & AccessFlags.ACC_SUPER) != 0;
+        this.sourceFile = cf.getSourceFile();
+
+        AttributeList attribs = method.getAttributes();
+        this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
+
+        AttributeList codeAttribs = attCode.getAttributes();
+
+        /*
+         * Combine all LineNumberTable attributes into one, with the
+         * combined result saved into the instance. The following code
+         * isn't particularly efficient for doing merges, but as far
+         * as I know, this situation rarely occurs "in the
+         * wild," so there's not much point in optimizing for it.
+         */
+        LineNumberList lineNumbers = LineNumberList.EMPTY;
+        if (keepLines) {
+            for (AttLineNumberTable lnt = (AttLineNumberTable)
+                     codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
+                 lnt != null;
+                 lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
+                lineNumbers = LineNumberList.concat(lineNumbers,
+                        lnt.getLineNumbers());
+            }
+        }
+        this.lineNumbers = lineNumbers;
+
+        LocalVariableList localVariables = LocalVariableList.EMPTY;
+        if (keepLocals) {
+            /*
+             * Do likewise (and with the same caveat) for
+             * LocalVariableTable and LocalVariableTypeTable attributes.
+             * This combines both of these kinds of attribute into a
+             * single LocalVariableList.
+             */
+            for (AttLocalVariableTable lvt = (AttLocalVariableTable)
+                     codeAttribs.findFirst(
+                             AttLocalVariableTable.ATTRIBUTE_NAME);
+                 lvt != null;
+                 lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
+                localVariables =
+                    LocalVariableList.concat(localVariables,
+                            lvt.getLocalVariables());
+            }
+
+            LocalVariableList typeList = LocalVariableList.EMPTY;
+            for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
+                     codeAttribs.findFirst(
+                             AttLocalVariableTypeTable.ATTRIBUTE_NAME);
+                 lvtt != null;
+                 lvtt =
+                     (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
+                typeList =
+                    LocalVariableList.concat(typeList,
+                            lvtt.getLocalVariables());
+            }
+
+            if (typeList.size() != 0) {
+                localVariables =
+                    LocalVariableList.mergeDescriptorsAndSignatures(
+                            localVariables, typeList);
+            }
+        }
+        this.localVariables = localVariables;
+    }
+
+    /** {@inheritDoc} */
+    public CstNat getNat() {
+        return method.getNat();
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getName() {
+        return method.getName();
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getDescriptor() {
+        return method.getDescriptor();
+    }
+
+    /** {@inheritDoc} */
+    public int getAccessFlags() {
+        return method.getAccessFlags();
+    }
+
+    /** {@inheritDoc} */
+    public AttributeList getAttributes() {
+        return method.getAttributes();
+    }
+
+    /** {@inheritDoc} */
+    public CstType getDefiningClass() {
+        return method.getDefiningClass();
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getEffectiveDescriptor() {
+        return method.getEffectiveDescriptor();
+    }
+
+    /**
+     * Gets whether the class that this method is part of is defined with
+     * <code>ACC_SUPER</code>.
+     * 
+     * @return the <code>ACC_SUPER</code> value
+     */
+    public boolean getAccSuper() {
+        return accSuper;
+    }
+
+    /**
+     * Gets the maximum stack size.
+     * 
+     * @return &gt;= 0; the maximum stack size
+     */
+    public int getMaxStack() {
+        return attCode.getMaxStack();
+    }
+
+    /**
+     * Gets the number of locals.
+     * 
+     * @return &gt;= 0; the number of locals
+     */
+    public int getMaxLocals() {
+        return attCode.getMaxLocals();
+    }
+
+    /**
+     * Gets the bytecode array.
+     * 
+     * @return non-null; the bytecode array
+     */
+    public BytecodeArray getCode() {
+        return attCode.getCode();
+    }
+
+    /**
+     * Gets the exception table.
+     * 
+     * @return non-null; the exception table
+     */
+    public ByteCatchList getCatches() {
+        return attCode.getCatches();
+    }
+
+    /**
+     * Gets the line number list.
+     * 
+     * @return non-null; the line number list
+     */
+    public LineNumberList getLineNumbers() {
+        return lineNumbers;
+    }
+
+    /**
+     * Gets the local variable list.
+     * 
+     * @return non-null; the local variable list
+     */
+    public LocalVariableList getLocalVariables() {
+        return localVariables;
+    }
+
+    /**
+     * Returns a {@link SourcePosition} instance corresponding to the
+     * given bytecode offset.
+     * 
+     * @param offset &gt;= 0; the bytecode offset
+     * @return non-null; an appropriate instance
+     */
+    public SourcePosition makeSourcePosistion(int offset) {
+        return new SourcePosition(sourceFile, offset,
+                                  lineNumbers.pcToLine(offset));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ExecutionStack.java b/dx/src/com/android/dx/cf/code/ExecutionStack.java
new file mode 100644
index 0000000..1a2b565
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ExecutionStack.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of a Java method execution stack.
+ * 
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public final class ExecutionStack extends MutabilityControl {
+    /** non-null; array of stack contents */
+    private final TypeBearer[] stack;
+
+    /**
+     * &gt;= 0; stack pointer (points one past the end) / current stack
+     * size 
+     */
+    private int stackPtr;
+
+    /**
+     * Constructs an instance. 
+     * 
+     * @param maxStack &gt;= 0; the maximum size of the stack for this
+     * instance
+     */
+    public ExecutionStack(int maxStack) {
+        super(maxStack != 0);
+        stack = new TypeBearer[maxStack];
+        stackPtr = 0;
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance.
+     * 
+     * @return non-null; the copy
+     */
+    public ExecutionStack copy() {
+        ExecutionStack result = new ExecutionStack(stack.length);
+
+        System.arraycopy(stack, 0, result.stack, 0, stack.length);
+        result.stackPtr = stackPtr;
+
+        return result;
+    }
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this instance.
+     * 
+     * @param ex non-null; the exception to annotate
+     */
+    public void annotate(ExceptionWithContext ex) {
+        int limit = stackPtr - 1;
+
+        for (int i = 0; i <= limit; i++) {
+            String idx = (i == limit) ? "top0" : Hex.u2(limit - i);
+
+            ex.addContext("stack[" + idx + "]: " +
+                          stackElementString(stack[i]));
+        }
+    }
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this stack with its initialized equivalent.
+     * 
+     * @param type non-null; type to replace
+     */
+    public void makeInitialized(Type type) {
+        if (stackPtr == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        Type initializedType = type.getInitializedType();
+
+        for (int i = 0; i < stackPtr; i++) {
+            if (stack[i] == type) {
+                stack[i] = initializedType;
+            }
+        }
+    }
+
+    /**
+     * Gets the maximum stack size for this instance.
+     * 
+     * @return &gt;= 0; the max stack size
+     */
+    public int getMaxStack() {
+        return stack.length;
+    }
+
+    /**
+     * Gets the current stack size.
+     * 
+     * @return &gt;= 0, &lt; getMaxStack(); the current stack size
+     */
+    public int size() {
+        return stackPtr;
+    }
+
+    /**
+     * Clears the stack. (That is, this method pops everything off.)
+     */
+    public void clear() {
+        throwIfImmutable();
+
+        for (int i = 0; i < stackPtr; i++) {
+            stack[i] = null;
+        }
+
+        stackPtr = 0;
+    }
+
+    /**
+     * Pushes a value of the given type onto the stack.
+     * 
+     * @param type non-null; type of the value
+     * @throws SimException thrown if there is insufficient room on the
+     * stack for the value
+     */
+    public void push(TypeBearer type) {
+        throwIfImmutable();
+
+        int category;
+
+        try {
+            type = type.getFrameType();
+            category = type.getType().getCategory();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("type == null");
+        }
+
+        if ((stackPtr + category) > stack.length) {
+            throwSimException("overflow");
+            return;
+        }
+
+        if (category == 2) {
+            stack[stackPtr] = null;
+            stackPtr++;
+        }
+
+        stack[stackPtr] = type;
+        stackPtr++;
+    }
+
+    /**
+     * Peeks at the <code>n</code>th element down from the top of the stack.
+     * <code>n == 0</code> means to peek at the top of the stack. Note that
+     * this will return <code>null</code> if the indicated element is the
+     * deeper half of a category-2 value.
+     * 
+     * @param n &gt;= 0; which element to peek at
+     * @return null-ok; the type of value stored at that element
+     * @throws SimException thrown if <code>n &gt;= size()</code> 
+     */
+    public TypeBearer peek(int n) {
+        if (n < 0) {
+            throw new IllegalArgumentException("n < 0");
+        }
+
+        if (n >= stackPtr) {
+            return throwSimException("underflow");
+        }
+
+        return stack[stackPtr - n - 1];
+    }
+
+    /**
+     * Peeks at the <code>n</code>th element down from the top of the
+     * stack, returning the type per se, as opposed to the
+     * <i>type-bearer</i>.  This method is just a convenient shorthand
+     * for <code>peek(n).getType()</code>.
+     * 
+     * @see #peek
+     */
+    public Type peekType(int n) {
+        return peek(n).getType();
+    }
+
+    /**
+     * Pops the top element off of the stack.
+     * 
+     * @return non-null; the type formerly on the top of the stack
+     * @throws SimException thrown if the stack is empty
+     */
+    public TypeBearer pop() {
+        throwIfImmutable();
+
+        TypeBearer result = peek(0);
+
+        stack[stackPtr - 1] = null;
+        stackPtr -= result.getType().getCategory();
+
+        return result;
+    }
+
+    /**
+     * Changes an element already on a stack. This method is useful in limited
+     * contexts, particularly when merging two instances. As such, it places
+     * the following restriction on its behavior: You may only replace
+     * values with other values of the same category.
+     * 
+     * @param n &gt;= 0; which element to change, where <code>0</code> is
+     * the top element of the stack
+     * @param type non-null; type of the new value
+     * @throws SimException thrown if <code>n &gt;= size()</code> or
+     * the action is otherwise prohibited
+     */
+    public void change(int n, TypeBearer type) {
+        throwIfImmutable();
+
+        try {
+            type = type.getFrameType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("type == null");
+        }
+
+        int idx = stackPtr - n - 1;
+        TypeBearer orig = stack[idx];
+
+        if ((orig == null) ||
+            (orig.getType().getCategory() != type.getType().getCategory())) {
+            throwSimException("incompatible substitution: " +
+                              stackElementString(orig) + " -> " +
+                              stackElementString(type));
+        }
+
+        stack[idx] = type;
+    }
+
+    /**
+     * Merges this stack with another stack. A new instance is returned if
+     * this merge results in a change. If no change results, this instance is
+     * returned.  See {@link Merger#mergeStack(ExecutionStack,ExecutionStack)
+     * Merger.mergeStack()}
+     *
+     * @param other non-null; a stack to merge with
+     * @return non-null; the result of the merge
+     */
+    public ExecutionStack merge(ExecutionStack other) {
+        try {
+            return Merger.mergeStack(this, other);
+        } catch (SimException ex) {
+            ex.addContext("underlay stack:");
+            this.annotate(ex);
+            ex.addContext("overlay stack:");
+            other.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Gets the string form for a stack element. This is the same as
+     * <code>toString()</code> except that <code>null</code> is converted
+     * to <code>"&lt;invalid&gt;"</code>.
+     * 
+     * @param type null-ok; the stack element
+     * @return non-null; the string form
+     */
+    private static String stackElementString(TypeBearer type) {
+        if (type == null) {
+            return "<invalid>";
+        }
+
+        return type.toString();
+    }
+
+    /**
+     * Throws a properly-formatted exception.
+     * 
+     * @param msg non-null; useful message
+     * @return never (keeps compiler happy)
+     */
+    private static TypeBearer throwSimException(String msg) {
+        throw new SimException("stack: " + msg);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Frame.java b/dx/src/com/android/dx/cf/code/Frame.java
new file mode 100644
index 0000000..a74d142
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Frame.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.IntList;
+
+/**
+ * Representation of a Java method execution frame. A frame consists
+ * of a set of locals and a value stack, and it can be told to act on
+ * them to load and store values between them and an "arguments /
+ * results" area.
+ */
+public final class Frame {
+    /** non-null; the locals */
+    private final LocalsArray locals;
+
+    /** non-null; the stack */
+    private final ExecutionStack stack;
+
+    /** null-ok; stack of labels of subroutines that this block is nested in */
+    private final IntList subroutines;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param locals non-null; the locals array to use
+     * @param stack non-null; the execution stack to use
+     */
+    private Frame(LocalsArray locals, ExecutionStack stack) {
+        this(locals, stack, IntList.EMPTY);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param locals non-null; the locals array to use
+     * @param stack non-null; the execution stack to use
+     * @param subroutines non-null; list of subroutine start labels for
+     * subroutines this frame is nested in
+     */
+    private Frame(LocalsArray locals,
+            ExecutionStack stack, IntList subroutines) {
+        if (locals == null) {
+            throw new NullPointerException("locals == null");
+        }
+
+        if (stack == null) {
+            throw new NullPointerException("stack == null");
+        }
+
+        subroutines.throwIfMutable();
+
+        this.locals = locals;
+        this.stack = stack;
+        this.subroutines = subroutines;
+    }
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as <code>null</code>s) and
+     * the stack starts out empty.
+     *
+     * @param maxLocals &gt;= 0; the maximum number of locals this instance
+     * can refer to
+     * @param maxStack &gt;= 0; the maximum size of the stack for this
+     * instance
+     */
+    public Frame(int maxLocals, int maxStack) {
+        this(new OneLocalsArray(maxLocals), new ExecutionStack(maxStack));
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance. The copy
+     * contains copies of the locals and stack (that is, it doesn't
+     * share them with the original).
+     *
+     * @return non-null; the copy
+     */
+    public Frame copy() {
+        return new Frame(locals.copy(), stack.copy(), subroutines);
+    }
+
+    /**
+     * Makes this instance immutable.
+     */
+    public void setImmutable() {
+        locals.setImmutable();
+        stack.setImmutable();
+        // "subroutines" is always immutable
+    }
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this frame with its initialized equivalent.
+     *
+     * @param type non-null; type to replace
+     */
+    public void makeInitialized(Type type) {
+        locals.makeInitialized(type);
+        stack.makeInitialized(type);
+    }
+
+    /**
+     * Gets the locals array for this instance.
+     *
+     * @return non-null; the locals array
+     */
+    public LocalsArray getLocals() {
+        return locals;
+    }
+
+    /**
+     * Gets the execution stack for this instance.
+     *
+     * @return non-null; the execution stack
+     */
+    public ExecutionStack getStack() {
+        return stack;
+    }
+
+    /**
+     * Returns the largest subroutine nesting this block may be in. An
+     * empty list is returned if this block is not in any subroutine.
+     * Subroutines are identified by the label of their start block. The
+     * list is ordered such that the deepest nesting (the actual subroutine
+     * this block is in) is the last label in the list.
+     *
+     * @return non-null; list as noted above
+     */
+    public IntList getSubroutines() {
+        return subroutines;
+    }
+
+    /**
+     * Initialize this frame with the method's parameters. Used for the first
+     * frame.
+     *
+     * @param params Type list of method parameters.
+     */
+    public void initializeWithParameters(StdTypeList params) {
+        int at = 0;
+        int sz = params.size();
+
+        for (int i = 0; i < sz; i++) {
+             Type one = params.get(i);
+             locals.set(at, one);
+             at += one.getCategory();
+        }
+    }
+
+    /**
+     * Returns a Frame instance representing the frame state that should
+     * be used when returning from a subroutine. The stack state of all
+     * subroutine invocations is identical, but the locals state may differ.
+     *
+     * @param startLabel &gt;=0; The label of the returning subroutine's
+     * start block
+     * @param subLabel &gt;=0; A calling label of a subroutine
+     * @return null-ok; an appropriatly-constructed instance, or null
+     * if label is not in the set
+     */
+    public Frame subFrameForLabel(int startLabel, int subLabel) {
+        LocalsArray subLocals = null;
+
+        if (locals instanceof LocalsArraySet) {
+            subLocals = ((LocalsArraySet)locals).subArrayForLabel(subLabel);
+        }
+
+        IntList newSubroutines;
+        try {
+            newSubroutines = subroutines.mutableCopy();
+
+            if (newSubroutines.pop() != startLabel) {
+                throw new RuntimeException("returning from invalid subroutine");
+            }
+            newSubroutines.setImmutable();
+        } catch (IndexOutOfBoundsException ex) {
+            throw new RuntimeException("returning from invalid subroutine");
+        } catch (NullPointerException ex) {
+            throw new NullPointerException("can't return from non-subroutine");
+        }
+       
+        return (subLocals == null) ? null
+                : new Frame(subLocals, stack, newSubroutines);
+    }
+
+    /**
+     * Merges two frames. If the merged result is the same as this frame,
+     * then this instance is returned.
+     *
+     * @param other non-null; another frame
+     * @return non-null; the result of merging the two frames
+     */
+    public Frame mergeWith(Frame other) {
+        LocalsArray resultLocals;
+        ExecutionStack resultStack;
+        IntList resultSubroutines;
+
+        resultLocals = getLocals().merge(other.getLocals());
+        resultStack = getStack().merge(other.getStack());
+        resultSubroutines = mergeSubroutineLists(other.subroutines);
+
+        resultLocals = adjustLocalsForSubroutines(
+                resultLocals, resultSubroutines);
+
+        if ((resultLocals == getLocals())
+                && (resultStack == getStack())
+                && subroutines == resultSubroutines) {
+            return this;
+        }
+
+        return new Frame(resultLocals, resultStack, resultSubroutines);
+    }
+
+    /**
+     * Merges this frame's subroutine lists with another. The result
+     * is the deepest common nesting (effectively, the common prefix of the
+     * two lists).
+     * 
+     * @param otherSubroutines label list of subroutine start blocks, from
+     * least-nested to most-nested.
+     * @return non-null; merged subroutine nest list as described above
+     */
+    private IntList mergeSubroutineLists(IntList otherSubroutines) {
+        if (subroutines.equals(otherSubroutines)) {
+            return subroutines;
+        }
+
+        IntList resultSubroutines = new IntList();
+
+        int szSubroutines = subroutines.size();
+        int szOthers = otherSubroutines.size();
+        for (int i = 0; i < szSubroutines && i < szOthers
+                && (subroutines.get(i) == otherSubroutines.get(i)); i++) {
+            resultSubroutines.add(i);
+        }
+
+        resultSubroutines.setImmutable();
+
+        return resultSubroutines;
+    }
+
+    /**
+     * Adjusts a locals array to account for a merged subroutines list.
+     * If a frame merge results in, effectively, a subroutine return through
+     * a throw then the current locals will be a LocalsArraySet that will
+     * need to be trimmed of all OneLocalsArray elements that relevent to
+     * the subroutine that is returning.
+     *
+     * @param locals non-null; LocalsArray from before a merge
+     * @param subroutines non-null; a label list of subroutine start blocks
+     * representing the subroutine nesting of the block being merged into.
+     * @return non-null; locals set appropriate for merge
+     */
+    private static LocalsArray adjustLocalsForSubroutines(
+            LocalsArray locals, IntList subroutines) {
+        if (! (locals instanceof LocalsArraySet)) {
+            // nothing to see here
+            return locals;
+        }
+
+        LocalsArraySet laSet = (LocalsArraySet)locals;
+
+        if (subroutines.size() == 0) {
+            /*
+             * We've merged from a subroutine context to a non-subroutine
+             * context, likely via a throw. Our successor will only need
+             * to consider the primary locals state, not the state of
+             * all possible subroutine paths.
+             */
+
+            return laSet.getPrimary();
+        }
+
+        /*
+         * It's unclear to me if the locals set needs to be trimmed here.
+         * If it does, then I believe it is all of the calling blocks
+         * in the subroutine at the end of "subroutines" passed into
+         * this method that should be removed.
+         */
+        return laSet;
+    }
+
+    /**
+     * Merges this frame with the frame of a subroutine caller at
+     * <code>predLabel</code>. Only called on the frame at the first
+     * block of a subroutine.
+     *
+     * @param other non-null; another frame
+     * @param subLabel label of subroutine start block
+     * @param predLabel label of calling block
+     * @return non-null; the result of merging the two frames
+     */
+    public Frame mergeWithSubroutineCaller(Frame other, int subLabel,
+            int predLabel) {
+        LocalsArray resultLocals;
+        ExecutionStack resultStack;
+
+        resultLocals = getLocals().mergeWithSubroutineCaller(
+                other.getLocals(), predLabel);
+        resultStack = getStack().merge(other.getStack());
+
+        IntList newOtherSubroutines = other.subroutines.mutableCopy();
+        newOtherSubroutines.add(subLabel);
+        newOtherSubroutines.setImmutable();
+
+        if ((resultLocals == getLocals())
+                && (resultStack == getStack())
+                && subroutines.equals(newOtherSubroutines)) {
+            return this;
+        }
+
+        IntList resultSubroutines;
+
+        if (subroutines.equals(newOtherSubroutines)) {
+            resultSubroutines = subroutines;
+        } else {
+            /*
+             * The new subroutines list should be the deepest of the two
+             * lists being merged, but the postfix of the resultant list
+             * must be equal to the shorter list.
+             */
+            IntList nonResultSubroutines;
+
+            if (subroutines.size() > newOtherSubroutines.size()) {
+                resultSubroutines = subroutines;
+                nonResultSubroutines = newOtherSubroutines;
+            } else {
+                resultSubroutines = newOtherSubroutines;
+                nonResultSubroutines = subroutines;
+            }
+
+            int szResult = resultSubroutines.size();
+            int szNonResult = nonResultSubroutines.size();
+
+            for (int i = szNonResult - 1; i >=0; i-- ) {
+                if (nonResultSubroutines.get(i)
+                        != resultSubroutines.get(
+                        i + (szResult - szNonResult))) {
+                    throw new
+                            RuntimeException("Incompatible merged subroutines");
+                }
+            }
+            
+        }
+
+        return new Frame(resultLocals, resultStack, resultSubroutines);
+    }
+
+    /**
+     * Makes a frame for a subroutine start block, given that this is the
+     * ending frame of one of the subroutine's calling blocks. Subroutine
+     * calls may be nested and thus may have nested locals state, so we
+     * start with an initial state as seen by the subroutine, but keep track
+     * of the individual locals states that will be expected when the individual
+     * subroutine calls return.
+     *
+     * @param subLabel label of subroutine start block
+     * @param callerLabel &gt;=0 label of the caller block where this frame
+     * came from.
+     * @return a new instance to begin a called subroutine.
+     */
+    public Frame makeNewSubroutineStartFrame(int subLabel, int callerLabel) {
+        IntList newSubroutines = subroutines.mutableCopy();
+        newSubroutines.add(subLabel);
+        Frame newFrame = new Frame(locals.getPrimary(), stack,
+                IntList.makeImmutable(subLabel));
+        return newFrame.mergeWithSubroutineCaller(this, subLabel, callerLabel);
+    }
+
+    /**
+     * Makes a new frame for an exception handler block invoked from this
+     * frame.
+     *
+     * @param exceptionClass exception that the handler block will handle
+     * @return new frame
+     */
+    public Frame makeExceptionHandlerStartFrame(CstType exceptionClass) {
+        ExecutionStack newStack = getStack().copy();
+
+        newStack.clear();
+        newStack.push(exceptionClass);
+
+        return new Frame(getLocals(), newStack, subroutines);
+    }
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this frame.
+     *
+     * @param ex non-null; the exception to annotate
+     */
+    public void annotate(ExceptionWithContext ex) {
+        locals.annotate(ex);
+        stack.annotate(ex);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LineNumberList.java b/dx/src/com/android/dx/cf/code/LineNumberList.java
new file mode 100644
index 0000000..35875b0
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LineNumberList.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "line number" entries, which are the contents of
+ * <code>LineNumberTable</code> attributes.
+ */
+public final class LineNumberList extends FixedSizeList {
+    /** non-null; zero-size instance */
+    public static final LineNumberList EMPTY = new LineNumberList(0);
+
+    /**
+     * Returns an instance which is the concatenation of the two given
+     * instances.
+     * 
+     * @param list1 non-null; first instance
+     * @param list2 non-null; second instance
+     * @return non-null; combined instance
+     */
+    public static LineNumberList concat(LineNumberList list1,
+                                        LineNumberList list2) {
+        if (list1 == EMPTY) {
+            // easy case
+            return list2;
+        }
+
+        int sz1 = list1.size();
+        int sz2 = list2.size();
+        LineNumberList result = new LineNumberList(sz1 + sz2);
+
+        for (int i = 0; i < sz1; i++) {
+            result.set(i, list1.get(i));
+        }
+
+        for (int i = 0; i < sz2; i++) {
+            result.set(sz1 + i, list2.get(i));
+        }
+
+        return result;
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param count the number of elements to be in the list
+     */
+    public LineNumberList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     * 
+     * @param n &gt;= 0; which item
+     * @return null-ok; the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param item non-null; the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param startPc &gt;= 0; start pc of this item
+     * @param lineNumber &gt;= 0; corresponding line number
+     */
+    public void set(int n, int startPc, int lineNumber) {
+        set0(n, new Item(startPc, lineNumber));
+    }
+
+    /**
+     * Gets the line number associated with the given address.
+     * 
+     * @param pc &gt;= 0; the address to look up
+     * @return &gt;= -1; the associated line number, or <code>-1</code> if
+     * none is known
+     */
+    public int pcToLine(int pc) {
+        /*
+         * Line number entries don't have to appear in any particular
+         * order, so we have to do a linear search. TODO: If
+         * this turns out to be a bottleneck, consider sorting the
+         * list prior to use.
+         */
+        int sz = size();
+        int bestPc = -1;
+        int bestLine = -1;
+
+        for (int i = 0; i < sz; i++) {
+            Item one = get(i);
+            int onePc = one.getStartPc();
+            if ((onePc <= pc) && (onePc > bestPc)) {
+                bestPc = onePc;
+                bestLine = one.getLineNumber();
+                if (bestPc == pc) {
+                    // We can't do better than this
+                    break;
+                }
+            }
+        }
+
+        return bestLine;
+    }
+
+    /**
+     * Item in a line number table.
+     */
+    public static class Item {
+        /** &gt;= 0; start pc of this item */
+        private final int startPc;
+
+        /** &gt;= 0; corresponding line number */
+        private final int lineNumber;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param startPc &gt;= 0; start pc of this item
+         * @param lineNumber &gt;= 0; corresponding line number
+         */
+        public Item(int startPc, int lineNumber) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (lineNumber < 0) {
+                throw new IllegalArgumentException("lineNumber < 0");
+            }
+
+            this.startPc = startPc;
+            this.lineNumber = lineNumber;
+        }
+
+        /**
+         * Gets the start pc of this item.
+         * 
+         * @return the start pc
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the line number of this item.
+         * 
+         * @return the line number
+         */
+        public int getLineNumber() {
+            return lineNumber;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalVariableList.java b/dx/src/com/android/dx/cf/code/LocalVariableList.java
new file mode 100644
index 0000000..1087603
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalVariableList.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "local variable" entries, which are the contents of
+ * <code>LocalVariableTable</code> and <code>LocalVariableTypeTable</code>
+ * attributes, as well as combinations of the two.
+ */
+public final class LocalVariableList extends FixedSizeList {
+    /** non-null; zero-size instance */
+    public static final LocalVariableList EMPTY = new LocalVariableList(0);
+
+    /**
+     * Returns an instance which is the concatenation of the two given
+     * instances. The result is immutable.
+     * 
+     * @param list1 non-null; first instance
+     * @param list2 non-null; second instance
+     * @return non-null; combined instance
+     */
+    public static LocalVariableList concat(LocalVariableList list1,
+                                           LocalVariableList list2) {
+        if (list1 == EMPTY) {
+            // easy case
+            return list2;
+        }
+
+        int sz1 = list1.size();
+        int sz2 = list2.size();
+        LocalVariableList result = new LocalVariableList(sz1 + sz2);
+
+        for (int i = 0; i < sz1; i++) {
+            result.set(i, list1.get(i));
+        }
+
+        for (int i = 0; i < sz2; i++) {
+            result.set(sz1 + i, list2.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Returns an instance which is the result of merging the two
+     * given instances, where one instance should have only type
+     * descriptors and the other only type signatures. The merged
+     * result is identical to the one with descriptors, except that
+     * any element whose {name, index, start, length} matches an
+     * element in the signature list gets augmented with the
+     * corresponding signature. The result is immutable.
+     * 
+     * @param descriptorList non-null; list with descriptors
+     * @param signatureList non-null; list with signatures
+     * @return non-null; the merged result
+     */
+    public static LocalVariableList mergeDescriptorsAndSignatures(
+            LocalVariableList descriptorList,
+            LocalVariableList signatureList) {
+        int signatureSize = signatureList.size();
+        int descriptorSize = descriptorList.size();
+        LocalVariableList result = new LocalVariableList(descriptorSize);
+
+        for (int i = 0; i < descriptorSize; i++) {
+            Item item = descriptorList.get(i);
+            Item signatureItem = signatureList.itemToLocal(item);
+            if (signatureItem != null) {
+                CstUtf8 signature = signatureItem.getSignature();
+                item = item.withSignature(signature);
+            }
+            result.set(i, item);
+        }        
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param count the number of elements to be in the list
+     */
+    public LocalVariableList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     * 
+     * @param n &gt;= 0; which item
+     * @return null-ok; the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param item non-null; the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     * 
+     * <p><b>Note:</b> At least one of <code>descriptor</code> or
+     * <code>signature</code> must be passed as non-null.</p>
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param startPc &gt;= 0; the start pc of this variable's scope
+     * @param length &gt;= 0; the length (in bytecodes) of this variable's
+     * scope
+     * @param name non-null; the variable's name
+     * @param descriptor null-ok; the variable's type descriptor
+     * @param signature null-ok; the variable's type signature
+     * @param index &gt;= 0; the variable's local index
+     */
+    public void set(int n, int startPc, int length, CstUtf8 name,
+            CstUtf8 descriptor, CstUtf8 signature, int index) {
+        set0(n, new Item(startPc, length, name, descriptor, signature, index));
+    }
+
+    /**
+     * Gets the local variable information in this instance which matches
+     * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
+     * in all respects but the type descriptor and signature, if any.
+     * 
+     * @param item non-null; local variable information to match
+     * @return null-ok; the corresponding local variable information stored
+     * in this instance, or <code>null</code> if there is no matching
+     * information
+     */
+    public Item itemToLocal(Item item) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Item one = (Item) get0(i);
+
+            if ((one != null) && one.matchesAllButType(item)) {
+                return one;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the local variable information associated with a given address
+     * and local index, if any. <b>Note:</b> In standard classfiles, a
+     * variable's start point is listed as the address of the instruction
+     * <i>just past</i> the one that sets the variable.
+     * 
+     * @param pc &gt;= 0; the address to look up
+     * @param index &gt;= 0; the local variable index
+     * @return null-ok; the associated local variable information, or
+     * <code>null</code> if none is known
+     */
+    public Item pcAndIndexToLocal(int pc, int index) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Item one = (Item) get0(i);
+
+            if ((one != null) && one.matchesPcAndIndex(pc, index)) {
+                return one;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Item in a local variable table.
+     */
+    public static class Item {
+        /** &gt;= 0; the start pc of this variable's scope */
+        private final int startPc;
+
+        /** &gt;= 0; the length (in bytecodes) of this variable's scope */
+        private final int length;
+
+        /** non-null; the variable's name */
+        private final CstUtf8 name;
+
+        /** null-ok; the variable's type descriptor */
+        private final CstUtf8 descriptor;
+
+        /** null-ok; the variable's type signature */
+        private final CstUtf8 signature;
+
+        /** &gt;= 0; the variable's local index */
+        private final int index;
+
+        /**
+         * Constructs an instance.
+         * 
+         * <p><b>Note:</b> At least one of <code>descriptor</code> or
+         * <code>signature</code> must be passed as non-null.</p>
+         * 
+         * @param startPc &gt;= 0; the start pc of this variable's scope
+         * @param length &gt;= 0; the length (in bytecodes) of this variable's
+         * scope
+         * @param name non-null; the variable's name
+         * @param descriptor null-ok; the variable's type descriptor
+         * @param signature null-ok; the variable's type signature
+         * @param index &gt;= 0; the variable's local index
+         */
+        public Item(int startPc, int length, CstUtf8 name,
+                CstUtf8 descriptor, CstUtf8 signature, int index) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (length < 0) {
+                throw new IllegalArgumentException("length < 0");
+            }
+
+            if (name == null) {
+                throw new NullPointerException("name == null");
+            }
+
+            if ((descriptor == null) && (signature == null)) {
+                throw new NullPointerException(
+                        "(descriptor == null) && (signature == null)");
+            }
+
+            if (index < 0) {
+                throw new IllegalArgumentException("index < 0");
+            }
+
+            this.startPc = startPc;
+            this.length = length;
+            this.name = name;
+            this.descriptor = descriptor;
+            this.signature = signature;
+            this.index = index;
+        }
+
+        /**
+         * Gets the start pc of this variable's scope.
+         * 
+         * @return &gt;= 0; the start pc of this variable's scope
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the length (in bytecodes) of this variable's scope.
+         * 
+         * @return &gt;= 0; the length (in bytecodes) of this variable's scope
+         */
+        public int getLength() {
+            return length;
+        }
+
+        /**
+         * Gets the variable's type descriptor.
+         *
+         * @return null-ok; the variable's type descriptor
+         */
+        public CstUtf8 getDescriptor() {
+            return descriptor;
+        }
+
+        /**
+         * Gets the variable's LocalItem, a (name, signature) tuple
+         *
+         * @return null-ok; the variable's type descriptor
+         */
+        public LocalItem getLocalItem() {
+            return LocalItem.make(name, signature);
+        }
+
+        /**
+         * Gets the variable's type signature. Private because if you need this,
+         * you want getLocalItem() instead.
+         *
+         * @return null-ok; the variable's type signature
+         */
+        private CstUtf8 getSignature() {
+            return signature;
+        }
+
+        /**
+         * Gets the variable's local index.
+         * 
+         * @return &gt;= 0; the variable's local index
+         */
+        public int getIndex() {
+            return index;
+        }
+
+        /**
+         * Gets the variable's type descriptor. This is a convenient shorthand
+         * for <code>Type.intern(getDescriptor().getString())</code>.
+         * 
+         * @return non-null; the variable's type
+         */
+        public Type getType() {
+            return Type.intern(descriptor.getString());
+        }
+
+        /**
+         * Constructs and returns an instance which is identical to this
+         * one, except that the signature is changed to the given value.
+         * 
+         * @param newSignature non-null; the new signature
+         * @return non-null; an appropriately-constructed instance
+         */
+        public Item withSignature(CstUtf8 newSignature) {
+            return new Item(startPc, length, name, descriptor, newSignature,
+                    index);
+        }
+
+        /**
+         * Gets whether this instance matches (describes) the given
+         * address and index.
+         * 
+         * @param pc &gt;= 0; the address in question
+         * @param index &gt;= 0; the local variable index in question
+         * @return <code>true</code> iff this instance matches <code>pc</code>
+         * and <code>index</code>
+         */
+        public boolean matchesPcAndIndex(int pc, int index) {
+            return (index == this.index) &&
+                (pc >= startPc) &&
+                (pc < (startPc + length));
+        }
+
+        /**
+         * Gets whether this instance matches (describes) the given
+         * other instance exactly in all fields except type descriptor and
+         * type signature.
+         * 
+         * @param other non-null; the instance to compare to
+         * @return <code>true</code> iff this instance matches
+         */
+        public boolean matchesAllButType(Item other) {
+            return (startPc == other.startPc)
+                && (length == other.length)
+                && (index == other.index)
+                && name.equals(other.name);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArray.java b/dx/src/com/android/dx/cf/code/LocalsArray.java
new file mode 100644
index 0000000..1c324ca
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArray.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ * 
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class LocalsArray extends MutabilityControl implements ToHuman {
+
+    /**
+     * Constructs an instance, explicitly indicating the mutability.
+     *
+     * @param mutable <code>true</code> if this instance is mutable
+     */   
+    protected LocalsArray(boolean mutable) {
+        super(mutable);
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance.
+     * 
+     * @return non-null; the copy
+     */
+    public abstract LocalsArray copy();
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this instance.
+     * 
+     * @param ex non-null; the exception to annotate
+     */
+    public abstract void annotate(ExceptionWithContext ex);
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this array with its initialized equivalent.
+     * 
+     * @param type non-null; type to replace
+     */
+    public abstract void makeInitialized(Type type);
+
+    /**
+     * Gets the maximum number of locals this instance can refer to.
+     * 
+     * @return the max locals
+     */
+    public abstract int getMaxLocals();
+    /**
+     * Sets the type stored at the given local index. If the given type
+     * is category-2, then (a) the index must be at least two less than
+     * <code>getMaxLocals()</code> and (b) the next index gets invalidated
+     * by the operation. In case of either category, if the <i>previous</i>
+     * local contains a category-2 value, then it too is invalidated by
+     * this operation.
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     * @param type non-null; new type for the local at <code>idx</code>
+     */
+    public abstract void set(int idx, TypeBearer type);
+
+    /**
+     * Sets the type for the local indicated by the given register spec
+     * to that register spec (which includes type and optional name
+     * information). This is identical to calling
+     * <code>set(spec.getReg(), spec)</code>.
+     * 
+     * @param spec non-null; register spec to use as the basis for the update
+     */
+    public abstract void set(RegisterSpec spec);
+
+    /**
+     * Invalidates the local at the given index.
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     */
+    public abstract void invalidate(int idx);
+
+    /**
+     * Gets the type stored at the given local index, or <code>null</code>
+     * if the given local is uninitialized / invalid.
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     * @return null-ok; the type of value stored in that local
+     */
+    public abstract TypeBearer getOrNull(int idx);
+
+    /**
+     * Gets the type stored at the given local index, only succeeding if
+     * the given local contains a valid type (though it is allowed to
+     * be an uninitialized instance).
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     * @return non-null; the type of value stored in that local
+     * @throws SimException thrown if <code>idx</code> is valid, but
+     * the contents are invalid
+     */
+    public abstract TypeBearer get(int idx);
+
+    /**
+     * Gets the type stored at the given local index, which is expected
+     * to be an initialized category-1 value.
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     * @return non-null; the type of value stored in that local
+     * @throws SimException thrown if <code>idx</code> is valid, but
+     * one of the following holds: (a) the local is invalid; (b) the local
+     * contains an uninitialized instance; (c) the local contains a
+     * category-2 value
+     */
+    public abstract TypeBearer getCategory1(int idx);
+
+    /**
+     * Gets the type stored at the given local index, which is expected
+     * to be a category-2 value.
+     * 
+     * @param idx &gt;= 0, &lt; getMaxLocals(); which local
+     * @return non-null; the type of value stored in that local
+     * @throws SimException thrown if <code>idx</code> is valid, but
+     * one of the following holds: (a) the local is invalid; (b) the local
+     * contains a category-1 value
+     */
+    public abstract TypeBearer getCategory2(int idx);
+
+    /**
+     * Merges this instance with <code>other</code>. If the merged result is
+     * the same as this instance, then this is returned (not a copy).
+     *
+     * @param other non-null; another LocalsArray
+     * @return non-null; the merge result, a new instance or this
+     */
+    public abstract LocalsArray merge(LocalsArray other);
+
+    /**
+     * Merges this instance with a <code>LocalsSet</code> from a subroutine
+     * caller. To be used when merging in the first block of a subroutine.
+     *
+     * @param other other non-null; another LocalsArray. The final locals
+     * state of a subroutine caller.
+     * @param predLabel the label of the subroutine caller block.
+     * @return non-null; the merge result, a new instance or this
+     */
+    public abstract LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel);
+
+    /**
+     * Gets the locals set appropriate for the current execution context.
+     * That is, if this is a <code>OneLocalsArray</code> instance, then return
+     * <code>this</code>, otherwise return <code>LocalsArraySet</code>'s
+     * primary.
+     *
+     * @return locals for this execution context.
+     */
+    protected abstract OneLocalsArray getPrimary();
+
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArraySet.java b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
new file mode 100644
index 0000000..9e24da9
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.ArrayList;
+
+/**
+ * Representation of a set of local variable arrays, with Java semantics.
+ * This peculiar case is to support in-method subroutines, which can
+ * have different locals sets for each caller.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class LocalsArraySet extends LocalsArray {
+
+    /**
+     * The primary LocalsArray represents the locals as seen from
+     * the subroutine itself, which is the merged representation of all the
+     * individual locals states.
+     */
+    private final OneLocalsArray primary;
+
+    /**
+     * Indexed by label of caller block: the locals specific to each caller's
+     * invocation of the subroutine.
+     */
+    private final ArrayList<LocalsArray> secondaries;
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as <code>null</code>s).
+     *
+     * @param maxLocals &gt;= 0; the maximum number of locals this instance
+     * can refer to
+     */
+    public LocalsArraySet(int maxLocals) {
+        super(maxLocals != 0);
+        primary = new OneLocalsArray(maxLocals);
+        secondaries = new ArrayList();
+    }
+
+    /**
+     * Constructs an instance with the specified primary and secondaries set.
+     *
+     * @param primary non-null; primary locals to use
+     * @param secondaries non-null; secondaries set, indexed by subroutine
+     * caller label.
+     */
+    public LocalsArraySet(OneLocalsArray primary,
+            ArrayList<LocalsArray> secondaries) {
+        super(primary.getMaxLocals() > 0);
+
+        this.primary = primary;
+        this.secondaries = secondaries;        
+    }
+
+    /**
+     * Constructs an instance which is a copy of another.
+     *
+     * @param toCopy non-null; instance to copy.
+     */
+    private LocalsArraySet(LocalsArraySet toCopy) {
+        super(toCopy.getMaxLocals() > 0);
+
+        primary = toCopy.primary.copy();
+        secondaries = new ArrayList(toCopy.secondaries.size());
+
+        int sz = toCopy.secondaries.size();
+        for(int i = 0; i < sz; i++) {
+            LocalsArray la = toCopy.secondaries.get(i);
+
+            if (la == null) {
+                secondaries.add(null);
+            } else {
+                secondaries.add(la.copy());
+            }
+        }
+    }
+
+
+    /** @inheritDoc */
+    @Override
+    public void setImmutable() {
+        primary.setImmutable();
+
+        for (LocalsArray la: secondaries) {
+            if (la != null) {
+                la.setImmutable();
+            }
+        }
+        super.setImmutable();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArray copy() {
+        return new LocalsArraySet(this);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void annotate(ExceptionWithContext ex) {
+        ex.addContext("(locals array set; primary)");
+        primary.annotate(ex);
+
+        int sz = secondaries.size();
+        for(int label = 0; label < sz; label++) {
+            LocalsArray la = secondaries.get(label);
+
+            if (la != null) {
+                ex.addContext("(locals array set: primary for caller "
+                        + Hex.u2(label) + ')');
+
+                la.getPrimary().annotate(ex);
+            }
+        }
+    }
+
+    /** {@inheritDoc*/
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("(locals array set; primary)\n");
+
+        sb.append(getPrimary().toHuman());
+        sb.append('\n');
+
+        int sz = secondaries.size();
+        for(int label = 0; label < sz; label++) {
+            LocalsArray la = secondaries.get(label);
+
+            if (la != null) {
+                sb.append("(locals array set: primary for caller "
+                        + Hex.u2(label) + ")\n");
+
+                sb.append(la.getPrimary().toHuman());
+                sb.append('\n');
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void makeInitialized(Type type) {
+        int len = primary.getMaxLocals();
+
+        if (len == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        primary.makeInitialized(type);
+
+        for (LocalsArray la: secondaries) {
+            if (la != null) {
+                la.makeInitialized(type);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public int getMaxLocals() {
+        return primary.getMaxLocals();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void set(int idx, TypeBearer type) {
+        throwIfImmutable();
+
+        primary.set(idx, type);
+
+        for (LocalsArray la: secondaries) {
+            if (la != null) {
+                la.set(idx, type);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void set(RegisterSpec spec) {
+        set(spec.getReg(), spec);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void invalidate(int idx) {
+        throwIfImmutable();
+
+        primary.invalidate(idx);
+
+        for (LocalsArray la: secondaries) {
+            if (la != null) {
+                la.invalidate(idx);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getOrNull(int idx) {
+        return primary.getOrNull(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer get(int idx) {
+        return primary.get(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getCategory1(int idx) {
+        return primary.getCategory1(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getCategory2(int idx) {
+        return primary.getCategory2(idx);
+    }
+
+    /**
+     * Merges this set with another <code>LocalsArraySet</code> instance.
+     *
+     * @param other non-null; to merge
+     * @return non-null; this instance if merge was a no-op, or
+     * new merged instance.
+     */
+    private LocalsArraySet mergeWithSet(LocalsArraySet other) {
+        OneLocalsArray newPrimary;
+        ArrayList<LocalsArray> newSecondaries;
+        boolean secondariesChanged = false;
+
+        newPrimary = primary.merge(other.getPrimary());
+
+        int sz1 = secondaries.size();
+        int sz2 = other.secondaries.size();
+        int sz = Math.max(sz1, sz2);
+        newSecondaries = new ArrayList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null);
+            LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null);
+            LocalsArray resultla = null;
+
+            if (la1 == la2) {
+                resultla = la1;
+            } else if (la1 == null) {
+                resultla = la2;
+            } else if (la2 == null) {
+                resultla = la1;
+            } else {
+                try {
+                    resultla = la1.merge(la2);
+                } catch (SimException ex) {
+                    ex.addContext(
+                            "Merging locals set for caller block " + Hex.u2(i));
+                }
+            }
+
+            secondariesChanged = secondariesChanged || (la1 != resultla);
+
+            newSecondaries.add(resultla);
+        }
+
+        if ((primary == newPrimary) && ! secondariesChanged ) {
+            return this;
+        }
+
+        return new LocalsArraySet(newPrimary, newSecondaries);
+    }
+
+    /**
+     * Merges this set with a <code>OneLocalsArray</code> instance.
+     *
+     * @param other non-null; to merge
+     * @return non-null; this instance if merge was a no-op, or
+     * new merged instance.
+     */
+    private LocalsArraySet mergeWithOne(OneLocalsArray other) {
+        OneLocalsArray newPrimary;
+        ArrayList<LocalsArray> newSecondaries;
+        boolean secondariesChanged = false;
+
+        newPrimary = primary.merge(other.getPrimary());
+        newSecondaries = new ArrayList(secondaries.size());
+
+        int sz = secondaries.size();
+        for (int i = 0; i < sz; i++) {
+            LocalsArray la = secondaries.get(i);
+            LocalsArray resultla = null;
+
+            if (la != null) {
+                try {
+                    resultla = la.merge(other);
+                } catch (SimException ex) {
+                    ex.addContext("Merging one locals against caller block "
+                                    + Hex.u2(i));
+                }
+            }
+
+            secondariesChanged = secondariesChanged || (la != resultla);
+
+            newSecondaries.add(resultla);
+        }
+
+        if ((primary == newPrimary) && ! secondariesChanged ) {
+            return this;
+        }
+
+        return new LocalsArraySet(newPrimary, newSecondaries);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArraySet merge(LocalsArray other) {
+        LocalsArraySet result;
+
+        try {
+            if (other instanceof LocalsArraySet) {
+                result = mergeWithSet((LocalsArraySet) other);
+            } else {
+                result = mergeWithOne((OneLocalsArray) other);
+            }
+        } catch (SimException ex) {
+            ex.addContext("underlay locals:");
+            annotate(ex);
+            ex.addContext("overlay locals:");
+            other.annotate(ex);
+            throw ex;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the <code>LocalsArray</code> instance for a specified subroutine
+     * caller label, or null if label has no locals associated with it.
+     *
+     * @param label &gt;=0 subroutine caller label
+     * @return null-ok; locals if available.
+     */
+    private LocalsArray getSecondaryForLabel(int label) {
+        if (label >= secondaries.size()) {
+            return null;
+        }
+
+        return secondaries.get(label);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel) {
+
+        LocalsArray mine = getSecondaryForLabel(predLabel);
+        LocalsArray newSecondary;
+        OneLocalsArray newPrimary;
+
+        newPrimary = primary.merge(other.getPrimary());
+
+        if (mine == other) {
+            newSecondary = mine;
+        } else if (mine == null) {
+            newSecondary = other;
+        } else {
+            newSecondary = mine.merge(other);
+        }
+
+        if ((newSecondary == mine) && (newPrimary == primary)) {
+            return this;
+        } else {
+            /*
+             * We're going to re-build a primary as a merge of all the
+             * secondaries.
+             */
+            newPrimary = null;
+
+            int szSecondaries = secondaries.size();
+            int sz = Math.max(predLabel + 1, szSecondaries);
+            ArrayList<LocalsArray> newSecondaries = new ArrayList(sz);
+            for (int i = 0; i < sz; i++) {
+                LocalsArray la = null;
+
+                if (i == predLabel) {
+                    /*
+                     * This LocalsArray always replaces any existing one,
+                     * since this is the result of a refined iteration.
+                     */
+                    la = newSecondary;
+                } else if (i < szSecondaries) {
+                    la = secondaries.get(i);
+                }
+
+                if (la != null) {
+                    if (newPrimary == null) {
+                        newPrimary = la.getPrimary();
+                    } else {
+                        newPrimary = newPrimary.merge(la.getPrimary());
+                    }
+                }
+
+                newSecondaries.add(la);
+            }
+
+            LocalsArraySet result
+                    = new LocalsArraySet(newPrimary, newSecondaries);
+            result.setImmutable();
+            return result;
+        }
+    }
+
+    /**
+     * Returns a LocalsArray instance representing the locals state that should
+     * be used when returning to a subroutine caller.
+     *
+     * @param subLabel &gt;= 0; A calling label of a subroutine
+     * @return null-ok; an instance for this subroutine, or null if subroutine
+     * is not in this set.
+     */
+    public LocalsArray subArrayForLabel(int subLabel) {
+        LocalsArray result = getSecondaryForLabel(subLabel);
+        return result;
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    protected OneLocalsArray getPrimary() {
+        return primary;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Machine.java b/dx/src/com/android/dx/cf/code/Machine.java
new file mode 100644
index 0000000..517a10d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Machine.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import java.util.ArrayList;
+
+/**
+ * Interface for machines capable of executing bytecode by acting
+ * upon a {@link Frame}. A machine conceptually contains four arbitrary-value
+ * argument slots, slots for several literal-value arguments, and slots for
+ * branch target information.
+ */
+public interface Machine {
+    /**
+     * Gets the effective prototype of the method that this instance is
+     * being used for. The <i>effective</i> prototype includes an initial
+     * <code>this</code> argument for instance methods.
+     * 
+     * @return non-null; the method prototype
+     */
+    public Prototype getPrototype();
+    
+    /**
+     * Clears the regular and auxiliary arguments area.
+     */
+    public void clearArgs();
+
+    /**
+     * Pops the given number of values from the stack (of either category),
+     * and store them in the arguments area, indicating that there are now
+     * that many arguments. Also, clear the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param count &gt;= 0; number of values to pop
+     */
+    public void popArgs(Frame frame, int count);
+
+    /**
+     * Pops values from the stack of the types indicated by the given
+     * <code>Prototype</code> (popped in reverse of the argument
+     * order, so the first prototype argument type is for the deepest
+     * element of the stack), and store them in the arguments area,
+     * indicating that there are now that many arguments. Also, clear
+     * the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param prototype non-null; prototype indicating arguments to pop
+     */
+    public void popArgs(Frame frame, Prototype prototype);
+
+    /**
+     * Pops a value from the stack of the indicated type, and store it
+     * in the arguments area, indicating that there are now that many
+     * arguments. Also, clear the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param type non-null; type of the argument
+     */
+    public void popArgs(Frame frame, Type type);
+
+    /**
+     * Pops values from the stack of the indicated types (popped in
+     * reverse argument order, so the first indicated type is for the
+     * deepest element of the stack), and store them in the arguments
+     * area, indicating that there are now that many arguments. Also,
+     * clear the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param type1 non-null; type of the first argument
+     * @param type2 non-null; type of the second argument
+     */
+    public void popArgs(Frame frame, Type type1, Type type2);
+
+    /**
+     * Pops values from the stack of the indicated types (popped in
+     * reverse argument order, so the first indicated type is for the
+     * deepest element of the stack), and store them in the arguments
+     * area, indicating that there are now that many arguments. Also,
+     * clear the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param type1 non-null; type of the first argument
+     * @param type2 non-null; type of the second argument
+     * @param type3 non-null; type of the third argument
+     */
+    public void popArgs(Frame frame, Type type1, Type type2, Type type3);
+
+    /**
+     * Loads the local variable with the given index as the sole argument in
+     * the arguments area. Also, clear the auxiliary arguments.
+     *
+     * @param frame non-null; frame to operate on
+     * @param idx &gt;= 0; the local variable index
+     */
+    public void localArg(Frame frame, int idx);
+
+    /**
+     * Indicates that the salient type of this operation is as
+     * given. This differentiates between, for example, the various
+     * arithmetic opcodes, which, by the time they hit a
+     * <code>Machine</code> are collapsed to the <code>int</code>
+     * variant. (See {@link BytecodeArray#parseInstruction} for
+     * details.)
+     *
+     * @param type non-null; the salient type of the upcoming operation
+     */
+    public void auxType(Type type);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack)
+     * argument of type <code>int</code>, with the given value.
+     *
+     * <p><b>Note:</b> Perhaps unintuitively, the stack manipulation
+     * ops (e.g., <code>dup</code> and <code>swap</code>) use this to
+     * indicate the result stack pattern with a straightforward hex
+     * encoding of the push order starting with least-significant
+     * nibbles getting pushed first). For example, an all-category-1
+     * <code>dup2_x1</code> sets this to <code>0x12312</code>, and the
+     * other form of that op sets this to
+     * <code>0x121</code>.</p>
+     *
+     * <p><b>Also Note:</b> For <code>switch*</code> instructions, this is
+     * used to indicate the padding value (which is only useful for
+     * verification).</p>
+     *
+     * @param value the argument value
+     */
+    public void auxIntArg(int value);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) object
+     * argument, with the value based on the given constant.
+     *
+     * <p><b>Note:</b> Some opcodes use both <code>int</code> and
+     * constant auxiliary arguments.</p>
+     *
+     * @param cst non-null; the constant containing / referencing
+     * the value
+     */
+    public void auxCstArg(Constant cst);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * indicating a branch target.
+     *
+     * @param target the argument value
+     */
+    public void auxTargetArg(int target);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * consisting of a <code>switch*</code> table.
+     *
+     * <p><b>Note:</b> This is generally used in conjunction with
+     * {@link #auxIntArg} (which holds the padding).</p>
+     *
+     * @param cases non-null; the list of key-target pairs, plus the default
+     * target
+     */
+    public void auxSwitchArg(SwitchList cases);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * consisting of a list of initial values for a newly created array.
+     *
+     * @param initValues non-null; the list of constant values to initialize
+     * the array
+     */
+    public void auxInitValues(ArrayList<Constant> initValues);
+
+    /**
+     * Indicates that the target of this operation is the given local.
+     *
+     * @param idx &gt;= 0; the local variable index
+     * @param type non-null; the type of the local
+     * @param local null-ok; the name and signature of the local, if known
+     */
+    public void localTarget(int idx, Type type, LocalItem local);
+
+    /**
+     * "Runs" the indicated opcode in an appropriate way, using the arguments
+     * area as appropriate, and modifying the given frame in response.
+     *
+     * @param frame non-null; frame to operate on
+     * @param offset &gt;= 0; byte offset in the method to the opcode being
+     * run
+     * @param opcode &gt;= 0; the opcode to run
+     */
+    public void run(Frame frame, int offset, int opcode);
+}
diff --git a/dx/src/com/android/dx/cf/code/Merger.java b/dx/src/com/android/dx/cf/code/Merger.java
new file mode 100644
index 0000000..adeaab2
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Merger.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Utility methods to merge various frame information.
+ */
+public final class Merger {
+    /**
+     * This class is uninstantiable.
+     */
+    private Merger() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Merges two locals arrays. If the merged result is the same as the first
+     * argument, then return the first argument (not a copy).
+     * 
+     * @param locals1 non-null; a locals array
+     * @param locals2 non-null; another locals array
+     * @return non-null; the result of merging the two locals arrays
+     */
+    public static OneLocalsArray mergeLocals(OneLocalsArray locals1,
+                                          OneLocalsArray locals2) {
+        if (locals1 == locals2) {
+            // Easy out.
+            return locals1;
+        }
+
+        int sz = locals1.getMaxLocals();
+        OneLocalsArray result = null;
+
+        if (locals2.getMaxLocals() != sz) {
+            throw new SimException("mismatched maxLocals values");
+        }
+
+        for (int i = 0; i < sz; i++) {
+            TypeBearer tb1 = locals1.getOrNull(i);
+            TypeBearer tb2 = locals2.getOrNull(i);
+            TypeBearer resultType = mergeType(tb1, tb2);
+            if (resultType != tb1) {
+                /*
+                 * We only need to do anything when the result differs
+                 * from what is in the first array, since that's what the
+                 * result gets initialized to.
+                 */
+                if (result == null) {
+                    result = locals1.copy();
+                }
+
+                if (resultType == null) {
+                    result.invalidate(i);
+                } else {
+                    result.set(i, resultType);
+                }
+            }
+        }
+
+        if (result == null) {
+            return locals1;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Merges two stacks. If the merged result is the same as the first
+     * argument, then return the first argument (not a copy).
+     * 
+     * @param stack1 non-null; a stack
+     * @param stack2 non-null; another stack
+     * @return non-null; the result of merging the two stacks
+     */
+    public static ExecutionStack mergeStack(ExecutionStack stack1,
+                                            ExecutionStack stack2) {
+        if (stack1 == stack2) {
+            // Easy out.
+            return stack1;
+        }
+
+        int sz = stack1.size();
+        ExecutionStack result = null;
+
+        if (stack2.size() != sz) {
+            throw new SimException("mismatched stack depths");
+        }
+
+        for (int i = 0; i < sz; i++) {
+            TypeBearer tb1 = stack1.peek(i);
+            TypeBearer tb2 = stack2.peek(i);
+            TypeBearer resultType = mergeType(tb1, tb2);
+            if (resultType != tb1) {
+                /*
+                 * We only need to do anything when the result differs
+                 * from what is in the first stack, since that's what the
+                 * result gets initialized to.
+                 */
+                if (result == null) {
+                    result = stack1.copy();
+                }
+
+                try {
+                    if (resultType == null) {
+                        throw new SimException("incompatible: " + tb1 + ", " +
+                                               tb2);
+                    } else {
+                        result.change(i, resultType);
+                    }
+                } catch (SimException ex) {
+                    ex.addContext("...while merging stack[" + Hex.u2(i) + "]");
+                    throw ex;
+                }
+            }
+        }
+
+        if (result == null) {
+            return stack1;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Merges two frame types.
+     * 
+     * @param ft1 non-null; a frame type
+     * @param ft2 non-null; another frame type
+     * @return non-null; the result of merging the two types
+     */
+    public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
+        if ((ft1 == null) || ft1.equals(ft2)) {
+            return ft1;
+        } else if (ft2 == null) {
+            return null;
+        } else {
+            Type type1 = ft1.getType();
+            Type type2 = ft2.getType();
+
+            if (type1 == type2) {
+                return type1;
+            } else if (type1.isReference() && type2.isReference()) {
+                if (type1 == Type.KNOWN_NULL) {
+                    /*
+                     * A known-null merges with any other reference type to
+                     * be that reference type.
+                     */
+                    return type2;
+                } else if (type2 == Type.KNOWN_NULL) {
+                    /*
+                     * The same as above, but this time it's type2 that's
+                     * the known-null.
+                     */
+                    return type1;
+                } else if (type1.isArray() && type2.isArray()) {
+                    TypeBearer componentUnion =
+                        mergeType(type1.getComponentType(),
+                                type2.getComponentType());
+                    if (componentUnion == null) {
+                        /*
+                         * At least one of the types is a primitive type,
+                         * so the merged result is just Object.
+                         */
+                        return Type.OBJECT;
+                    }
+                    return ((Type) componentUnion).getArrayType();
+                } else {
+                    /*
+                     * All other unequal reference types get merged to be
+                     * Object in this phase. This is fine here, but it
+                     * won't be the right thing to do in the verifier.
+                     */
+                    return Type.OBJECT;
+                }
+            } else if (type1.isIntlike() && type2.isIntlike()) {
+                /*
+                 * Merging two non-identical int-like types results in
+                 * the type int.
+                 */
+                return Type.INT;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Returns whether the given supertype is possibly assignable from
+     * the given subtype. This takes into account primitiveness,
+     * int-likeness, known-nullness, and array dimensions, but does
+     * not assume anything about class hierarchy other than that the
+     * type <code>Object</code> is the supertype of all reference
+     * types and all arrays are assignable to
+     * <code>Serializable</code> and <code>Cloneable</code>.
+     * 
+     * @param supertypeBearer non-null; the supertype
+     * @param subtypeBearer non-null; the subtype
+     */
+    public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
+            TypeBearer subtypeBearer) {
+        Type supertype = supertypeBearer.getType();
+        Type subtype = subtypeBearer.getType();
+
+        if (supertype.equals(subtype)) {
+            // Easy out.
+            return true;
+        }
+
+        int superBt = supertype.getBasicType();
+        int subBt = subtype.getBasicType();
+
+        // Treat return types as Object for the purposes of this method.
+
+        if (superBt == Type.BT_ADDR) {
+            supertype = Type.OBJECT;
+            superBt = Type.BT_OBJECT;
+        }
+
+        if (subBt == Type.BT_ADDR) {
+            subtype = Type.OBJECT;
+            subBt = Type.BT_OBJECT;
+        }
+
+        if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
+            /*
+             * No two distinct primitive types are assignable in this sense,
+             * unless they are both int-like.
+             */
+            return supertype.isIntlike() && subtype.isIntlike();
+        }
+
+        // At this point, we know both types are reference types.
+
+        if (supertype == Type.KNOWN_NULL) {
+            /*
+             * A known-null supertype is only assignable from another
+             * known-null (handled in the easy out at the top of the
+             * method).
+             */
+            return false;
+        } else if (subtype == Type.KNOWN_NULL) {
+            /*
+             * A known-null subtype is in fact assignable to any
+             * reference type.
+             */
+            return true;
+        } else if (supertype == Type.OBJECT) {
+            /*
+             * Object is assignable from any reference type.
+             */
+            return true;
+        } else if (supertype.isArray()) {
+            // The supertype is an array type.
+            if (! subtype.isArray()) {
+                // The subtype isn't an array, and so can't be assignable.
+                return false;
+            }
+
+            /*
+             * Strip off as many matched component types from both
+             * types as possible, and check the assignability of the
+             * results.
+             */
+            do {
+                supertype = supertype.getComponentType();
+                subtype = subtype.getComponentType();
+            } while (supertype.isArray() && subtype.isArray());
+
+            return isPossiblyAssignableFrom(supertype, subtype);
+        } else if (subtype.isArray()) {
+            /*
+             * Other than Object (handled above), array types are
+             * assignable only to Serializable and Cloneable.
+             */
+            return (supertype == Type.SERIALIZABLE) ||
+                (supertype == Type.CLONEABLE);
+        } else {
+            /*
+             * All other unequal reference types are considered at
+             * least possibly assignable.
+             */
+            return true;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/OneLocalsArray.java b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
new file mode 100644
index 0000000..3a590a1
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class OneLocalsArray extends LocalsArray {
+    /** non-null; actual array */
+    private final TypeBearer[] locals;
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as <code>null</code>s).
+     *
+     * @param maxLocals &gt;= 0; the maximum number of locals this instance
+     * can refer to
+     */
+    public OneLocalsArray(int maxLocals) {
+        super(maxLocals != 0);
+        locals = new TypeBearer[maxLocals];
+    }
+
+    /** @inheritDoc */
+    public OneLocalsArray copy() {
+        OneLocalsArray result = new OneLocalsArray(locals.length);
+
+        System.arraycopy(locals, 0, result.locals, 0, locals.length);
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public void annotate(ExceptionWithContext ex) {
+        for (int i = 0; i < locals.length; i++) {
+            TypeBearer type = locals[i];
+            String s = (type == null) ? "<invalid>" : type.toString();
+            ex.addContext("locals[" + Hex.u2(i) + "]: " + s);
+        }
+    }
+
+    /** {@inheritDoc*/
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < locals.length; i++) {
+            TypeBearer type = locals[i];
+            String s = (type == null) ? "<invalid>" : type.toString();
+            sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n");
+        }
+
+        return sb.toString();
+    }
+
+    /** @inheritDoc */
+    public void makeInitialized(Type type) {
+        int len = locals.length;
+
+        if (len == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        Type initializedType = type.getInitializedType();
+
+        for (int i = 0; i < len; i++) {
+            if (locals[i] == type) {
+                locals[i] = initializedType;
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int getMaxLocals() {
+        return locals.length;
+    }
+
+    /** @inheritDoc */
+    public void set(int idx, TypeBearer type) {
+        throwIfImmutable();
+
+        try {
+            type = type.getFrameType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception
+            throw new NullPointerException("type == null");
+        }
+
+        if (idx < 0) {
+            throw new IndexOutOfBoundsException("idx < 0");
+        }
+
+        // Make highest possible out-of-bounds check happen first.
+        if (type.getType().isCategory2()) {
+            locals[idx + 1] = null;
+        }
+
+        locals[idx] = type;
+
+        if (idx != 0) {
+            TypeBearer prev = locals[idx - 1];
+            if ((prev != null) && prev.getType().isCategory2()) {
+                locals[idx - 1] = null;
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public void set(RegisterSpec spec) {
+        set(spec.getReg(), spec);
+    }
+
+    /** @inheritDoc */
+    public void invalidate(int idx) {
+        throwIfImmutable();
+        locals[idx] = null;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getOrNull(int idx) {
+        return locals[idx];
+    }
+
+    /** @inheritDoc */
+    public TypeBearer get(int idx) {
+        TypeBearer result = locals[idx];
+
+        if (result == null) {
+            return throwSimException(idx, "invalid");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getCategory1(int idx) {
+        TypeBearer result = get(idx);
+        Type type = result.getType();
+
+        if (type.isUninitialized()) {
+            return throwSimException(idx, "uninitialized instance");
+        }
+
+        if (type.isCategory2()) {
+            return throwSimException(idx, "category-2");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getCategory2(int idx) {
+        TypeBearer result = get(idx);
+
+        if (result.getType().isCategory1()) {
+            return throwSimException(idx, "category-1");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArray merge(LocalsArray other) {
+        if (other instanceof OneLocalsArray) {
+            return merge((OneLocalsArray)other);
+        } else { //LocalsArraySet
+            // LocalsArraySet knows how to merge me.
+            return other.merge(this);
+        }
+    }
+
+    /**
+     * Merges this OneLocalsArray instance with another OneLocalsArray
+     * instance. A more-refined version of {@link #merge(LocalsArray) merge}
+     * which is called by that method when appropriate.
+     *
+     * @param other locals array with which to merge
+     * @return this instance if merge was a no-op, or a new instance if
+     * the merge resulted in a change.
+     */
+    public OneLocalsArray merge(OneLocalsArray other) {
+        try {
+            return Merger.mergeLocals(this, other);
+        } catch (SimException ex) {
+            ex.addContext("underlay locals:");
+            annotate(ex);
+            ex.addContext("overlay locals:");
+            other.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel) {
+
+        LocalsArraySet result = new LocalsArraySet(getMaxLocals());
+        return result.mergeWithSubroutineCaller(other, predLabel);
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    protected OneLocalsArray getPrimary() {
+        return this;
+    }
+
+    /**
+     * Throws a properly-formatted exception.
+     *
+     * @param idx the salient local index
+     * @param msg non-null; useful message
+     * @return never (keeps compiler happy)
+     */
+    private static TypeBearer throwSimException(int idx, String msg) {
+        throw new SimException("local " + Hex.u2(idx) + ": " + msg);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ReturnAddress.java b/dx/src/com/android/dx/cf/code/ReturnAddress.java
new file mode 100644
index 0000000..c69253c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ReturnAddress.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a subroutine return address. In Java verification,
+ * somewhat counterintuitively, the salient bit of information you need to
+ * know about a return address is the <i>start address</i> of the subroutine
+ * being returned from, not the address being returned <i>to</i>, so that's
+ * what instances of this class hang onto.
+ */
+public final class ReturnAddress implements TypeBearer {
+    /** &gt;= 0; the start address of the subroutine being returned from */
+    private final int subroutineAddress;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param subroutineAddress &gt;= 0; the start address of the
+     * subroutine being returned from
+     */
+    public ReturnAddress(int subroutineAddress) {
+        if (subroutineAddress < 0) {
+            throw new IllegalArgumentException("subroutineAddress < 0");
+        }
+
+        this.subroutineAddress = subroutineAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return ("<addr:" + Hex.u2(subroutineAddress) + ">");
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toString();
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.RETURN_ADDRESS;
+    }
+
+    /** {@inheritDoc} */
+    public TypeBearer getFrameType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicType() {
+        return Type.RETURN_ADDRESS.getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicFrameType() {
+        return Type.RETURN_ADDRESS.getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isConstant() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof ReturnAddress)) {
+            return false;
+        }
+
+        return subroutineAddress == ((ReturnAddress) other).subroutineAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return subroutineAddress;
+    }
+
+    /**
+     * Gets the subroutine address.
+     * 
+     * @return &gt;= 0; the subroutine address
+     */
+    public int getSubroutineAddress() {
+        return subroutineAddress;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Ropper.java b/dx/src/com/android/dx/cf/code/Ropper.java
new file mode 100644
index 0000000..f3eecab
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Ropper.java
@@ -0,0 +1,1667 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+
+/**
+ * Utility that converts a basic block list into a list of register-oriented
+ * blocks.
+ */
+public final class Ropper {
+    /** label offset for the parameter assignment block */
+    private static final int PARAM_ASSIGNMENT = -1;
+
+    /** label offset for the return block */
+    private static final int RETURN = -2;
+
+    /** label offset for the synchronized method final return block */
+    private static final int SYNCH_RETURN = -3;
+
+    /** label offset for the first synchronized method setup block */
+    private static final int SYNCH_SETUP_1 = -4;
+
+    /** label offset for the second synchronized method setup block */
+    private static final int SYNCH_SETUP_2 = -5;
+
+    /**
+     * label offset for the first synchronized method exception
+     * handler block 
+     */
+    private static final int SYNCH_CATCH_1 = -6;
+
+    /**
+     * label offset for the second synchronized method exception
+     * handler block 
+     */
+    private static final int SYNCH_CATCH_2 = -7;
+
+    /** number of special label offsets */
+    private static final int SPECIAL_LABEL_COUNT = 7;
+
+    /** non-null; method being converted */
+    private final ConcreteMethod method;
+
+    /** non-null; original block list */
+    private final ByteBlockList blocks;
+
+    /** max locals of the method */
+    private final int maxLocals;
+
+    /** max label (exclusive) of any original bytecode block */
+    private final int maxLabel;
+
+    /** non-null; simulation machine to use */
+    private final RopperMachine machine;
+
+    /** non-null; simulator to use */
+    private final Simulator sim;
+
+    /**
+     * non-null; sparse array mapping block labels to initial frame contents,
+     * if known 
+     */
+    private final Frame[] startFrames;
+
+    /** non-null; output block list in-progress */
+    private final ArrayList<BasicBlock> result;
+
+    /**
+     * non-null; list of subroutine-nest labels
+     * (See {@link Frame#getSubroutines} associated with each result block.
+     * Parallel to {@link Ropper#result}. 
+     */
+    private final ArrayList<IntList> resultSubroutines;
+
+    /**
+     * non-null; for each block (by label) that is used as an exception
+     * handler, the type of exception it catches 
+     */
+    private final Type[] catchTypes;
+
+    /**
+     * whether an exception-handler block for a synchronized method was
+     * ever required 
+     */
+    private boolean synchNeedsExceptionHandler;
+
+    /** non-null; list of subroutines indexed by label of start address */
+    private final Subroutine subroutines[];
+
+    /** true if <code>subroutines</code> is non-empty */
+    private boolean hasSubroutines;
+
+    /**
+     * Keeps track of subroutines that exist in java form and are inlined in
+     * Rop form.
+     */
+    private class Subroutine {
+        /** list of all blocks that jsr to this subroutine */
+        private BitSet callerBlocks;
+        /** List of all blocks that return from this subroutine */
+        private BitSet retBlocks;
+        /** first block in this subroutine */
+        private int startBlock;
+
+        /**
+         * Constructs instance.
+         *
+         * @param startBlock First block of the subroutine.
+         */
+        Subroutine(int startBlock) {
+            this.startBlock = startBlock;
+            retBlocks = new BitSet(maxLabel);
+            callerBlocks = new BitSet(maxLabel);
+            hasSubroutines = true;
+        }
+
+        /**
+         * Constructs instance.
+         *
+         * @param startBlock First block of the subroutine.
+         * @param retBlock one of the ret blocks (final blocks) of this
+         * subroutine.
+         */
+        Subroutine(int startBlock, int retBlock) {
+            this(startBlock);
+            addRetBlock(retBlock);
+        }
+
+        /**
+         * @return &gt;= 0; the label of the subroutine's start block.
+         */
+        int getStartBlock() {
+            return startBlock;
+        }
+
+        /**
+         * Adds a label to the list of ret blocks (final blocks) for this
+         * subroutine.
+         * 
+         * @param retBlock ret block label
+         */
+        void addRetBlock(int retBlock) {
+            retBlocks.set(retBlock);
+        }
+
+        /**
+         * Adds a label to the list of caller blocks for this subroutine.
+         *
+         * @param label a block that invokes this subroutine.
+         */
+        void addCallerBlock(int label) {
+            callerBlocks.set(label);
+        }
+
+        /**
+         * Generates a list of subroutine successors. Note: successor blocks
+         * could be listed more than once. This is ok, because this successor
+         * list (and the block it's associated with) will be copied and inlined
+         * before we leave the ropper. Redundent successors will result in
+         * redundent (no-op) merges.
+         * 
+         * @return all currently known successors
+         * (return destinations) for that subroutine
+         */
+        IntList getSuccessors() {
+            IntList successors = new IntList(callerBlocks.size());
+
+            /*
+             * For each subroutine caller, get it's target. If the target is us,
+             * add the ret target (subroutine successor) to our list
+             */
+
+            for(int label = callerBlocks.nextSetBit(0); label >= 0 
+                    ; label = callerBlocks.nextSetBit(label+1)) {
+
+                BasicBlock subCaller = labelToBlock(label);
+                successors.add(subCaller.getSuccessors().get(0));
+            }
+
+            successors.setImmutable();
+           
+            return successors;
+        }
+
+        /**
+         * Merges the specified frame into this subroutine's successors,
+         * setting <code>workSet</code> as appropriate. To be called with
+         * the frame of a subroutine ret block.
+         *
+         * @param frame non-null; frame from ret block to merge
+         * @param workSet non-null; workset to update
+         */
+        void mergeToSuccessors(Frame frame, int[] workSet) {
+            int sz = callerBlocks.size();
+
+            for(int label = callerBlocks.nextSetBit(0); label >= 0
+                    ; label = callerBlocks.nextSetBit(label+1)) {
+
+                BasicBlock subCaller = labelToBlock(label);
+                int succLabel = subCaller.getSuccessors().get(0);
+
+                Frame subFrame = frame.subFrameForLabel(startBlock, label);
+
+                if (subFrame != null) {
+                    mergeAndWorkAsNecessary(succLabel, -1, null,
+                            subFrame, workSet);
+                } else {
+                    Bits.set(workSet, label);
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts a {@link ConcreteMethod} to a {@link RopMethod}.
+     * 
+     * @param method non-null; method to convert
+     * @param advice non-null; translation advice to use
+     * @return non-null; the converted instance
+     */
+    public static RopMethod convert(ConcreteMethod method,
+            TranslationAdvice advice) {
+        try {
+            Ropper r = new Ropper(method, advice);
+            r.doit();
+            return r.getRopMethod();
+        } catch (SimException ex) {
+            ex.addContext("...while working on method " +
+                          method.getNat().toHuman());
+            throw ex;
+        }
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable; use
+     * {@link #convert}.
+     * 
+     * @param method non-null; method to convert
+     * @param advice non-null; translation advice to use
+     */
+    private Ropper(ConcreteMethod method, TranslationAdvice advice) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (advice == null) {
+            throw new NullPointerException("advice == null");
+        }
+
+        this.method = method;
+        this.blocks = BasicBlocker.identifyBlocks(method);
+        this.maxLabel = blocks.getMaxLabel();
+        this.maxLocals = method.getMaxLocals();
+        this.machine = new RopperMachine(this, method, advice);
+        this.sim = new Simulator(machine, method);
+        this.startFrames = new Frame[maxLabel];
+        this.subroutines = new Subroutine[maxLabel];
+
+        /*
+         * The "* 2 + 10" below is to conservatively believe that every
+         * block is an exception handler target and should also
+         * take care of enough other possible extra overhead such that
+         * the underlying array is unlikely to need resizing.
+         */
+        this.result = new ArrayList<BasicBlock>(blocks.size() * 2 + 10);
+        this.resultSubroutines = new ArrayList<IntList>(blocks.size() * 2 + 10);
+
+        this.catchTypes = new Type[maxLabel];
+        this.synchNeedsExceptionHandler = false;
+
+        /*
+         * Set up the first stack frame with the right limits, but leave it
+         * empty here (to be filled in outside of the constructor).
+         */
+        startFrames[0] = new Frame(maxLocals, method.getMaxStack());
+    }
+
+    /**
+     * Gets the first (lowest) register number to use as the temporary
+     * area when unwinding stack manipulation ops.
+     * 
+     * @return &gt;= 0; the first register to use
+     */
+    /*package*/ int getFirstTempStackReg() {
+        /*
+         * We use the register that is just past the deepest possible
+         * stack element, plus one if the method is synchronized to
+         * avoid overlapping with the synch register. We don't need to
+         * do anything else special at this level, since later passes
+         * will merely notice the highest register used by explicit
+         * inspection.
+         */
+        int regCount = getNormalRegCount();
+        return isSynchronized() ? regCount + 1 : regCount;
+    }
+
+    /**
+     * Gets the label for the exception handler setup block corresponding
+     * to the given label.
+     * 
+     * @param label &gt;= 0; the original label
+     * @return &gt;= 0; the corresponding exception handler setup label
+     */
+    private int getExceptionSetupLabel(int label) {
+        return maxLabel + label;
+    }
+
+    /**
+     * Gets the label for the given special-purpose block. The given label
+     * should be one of the static constants defined by this class.
+     * 
+     * @param label &lt; 0; the special label constant
+     * @return &gt;= 0; the actual label value to use
+     */
+    private int getSpecialLabel(int label) {
+        /*
+         * The label is bitwise-complemented so that mistakes where
+         * LABEL is used instead of getSpecialLabel(LABEL) cause a
+         * failure at block construction time, since negative labels
+         * are illegal. We multiply maxLabel by 2 since 0..maxLabel
+         * (exclusive) are the original blocks and
+         * maxLabel..(maxLabel*2) are reserved for exception handler
+         * setup blocks (see getExceptionSetupLabel(), above).
+         */
+        return (maxLabel * 2) + ~label;
+    }
+
+    /**
+     * Gets the minimum label for unreserved use.
+     * 
+     * @return &gt;= 0; the minimum label
+     */
+    private int getMinimumUnreservedLabel() {
+        /*
+         * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are
+         * reserved for particular uses.
+         */
+
+        return (maxLabel * 2) + SPECIAL_LABEL_COUNT;
+    }
+
+    /**
+     * Gets an arbitrary unreserved and available label.
+     * 
+     * @return &gt;= 0; the label
+     */
+    private int getAvailableLabel() {
+        int candidate = getMinimumUnreservedLabel();
+
+        for (BasicBlock bb : result) {
+            int label = bb.getLabel();
+            if (label >= candidate) {
+                candidate = label + 1;
+            }
+        }
+
+        return candidate;
+    }
+
+    /**
+     * Gets whether the method being translated is synchronized.
+     * 
+     * @return whether the method being translated is synchronized
+     */
+    private boolean isSynchronized() {
+        int accessFlags = method.getAccessFlags();
+        return (accessFlags & AccessFlags.ACC_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Gets whether the method being translated is static.
+     * 
+     * @return whether the method being translated is static
+     */
+    private boolean isStatic() {
+        int accessFlags = method.getAccessFlags();
+        return (accessFlags & AccessFlags.ACC_STATIC) != 0;
+    }
+
+    /**
+     * Gets the total number of registers used for "normal" purposes (i.e.,
+     * for the straightforward translation from the original Java).
+     * 
+     * @return &gt;= 0; the total number of registers used
+     */
+    private int getNormalRegCount() {
+        return maxLocals + method.getMaxStack();
+    }
+
+    /**
+     * Gets the register spec to use to hold the object to synchronize on,
+     * for a synchronized method.
+     * 
+     * @return non-null; the register spec
+     */
+    private RegisterSpec getSynchReg() {
+        /*
+         * We use the register that is just past the deepest possible
+         * stack element. We don't need to do anything else special at
+         * this level, since later passes will merely notice the
+         * highest register used by explicit inspection.
+         */
+        return RegisterSpec.make(getNormalRegCount(), Type.OBJECT);
+    }
+
+    /**
+     * Searches {@link #result} for a block with the given label. Return its
+     * index if found, or return <code>-1</code> if there is no such block.
+     * 
+     * @param label the label to look for
+     * @return &gt;= -1; the index for the block with the given label or
+     * <code>-1</code> if there is no such block
+     */
+    private int labelToResultIndex(int label) {
+        int sz = result.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = result.get(i);
+            if (one.getLabel() == label) {
+                return i;
+            }
+        }
+
+        return -1;
+    }        
+
+    /**
+     * Searches {@link #result} for a block with the given label. Return it if
+     * found, or throw an exception if there is no such block.
+     * 
+     * @param label the label to look for
+     * @return non-null; the block with the given label
+     */
+    private BasicBlock labelToBlock(int label) {
+        int idx = labelToResultIndex(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label " +
+                    Hex.u2(label));
+        }
+
+        return result.get(idx);
+    }
+
+    /**
+     * Adds a block to the output result.
+     * 
+     * @param block non-null; the block to add
+     * @param subroutines non-null; subroutine label list as described in
+     * {@link Frame#getSubroutines}
+     */
+    private void addBlock(BasicBlock block, IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+    }
+
+    /**
+     * Adds or replace a block in the output result. If this is a
+     * replacement, then any extra blocks that got added with the
+     * original get removed as a result of calling this method.
+     * 
+     * @param block non-null; the block to add or replace
+     * @param subroutines non-null; subroutine label list as described in
+     * {@link Frame#getSubroutines}
+     * @return <code>true</code> if the block was replaced or
+     * <code>false</code> if it was added for the first time
+     */
+    private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        int idx = labelToResultIndex(block.getLabel());
+        boolean ret;
+
+        if (idx < 0) {
+            ret = false;
+        } else {
+            /*
+             * We are replacing a pre-existing block, so find any
+             * blocks that got added as part of the original and
+             * remove those too. Such blocks are (possibly indirect)
+             * successors of this block which are out of the range of
+             * normally-translated blocks.
+             */
+            removeBlockAndSpecialSuccessors(idx);
+            ret = true;
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+        return ret;
+    }
+
+    /**
+     * Adds or replaces a block in the output result. Do not delete
+     * any successors.
+     *
+     * @param block non-null; the block to add or replace
+     * @param subroutines non-null; subroutine label list as described in
+     * {@link Frame#getSubroutines}
+     * @return <code>true</code> if the block was replaced or
+     * <code>false</code> if it was added for the first time
+     */
+    private boolean addOrReplaceBlockNoDelete(BasicBlock block,
+            IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        int idx = labelToResultIndex(block.getLabel());
+        boolean ret;
+
+        if (idx < 0) {
+            ret = false;
+        } else {
+            result.remove(idx);
+            resultSubroutines.remove(idx);
+            ret = true;
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+        return ret;
+    }
+
+    /**
+     * Helper for {@link #addOrReplaceBlock} which recursively removes
+     * the given block and all blocks that are (direct and indirect)
+     * successors of it whose labels indicate that they are not in the
+     * normally-translated range.
+     * 
+     * @param idx non-null; block to remove (etc.)
+     */
+    private void removeBlockAndSpecialSuccessors(int idx) {
+        int minLabel = getMinimumUnreservedLabel();
+        BasicBlock block = result.get(idx);
+        IntList successors = block.getSuccessors();
+        int sz = successors.size();
+
+        result.remove(idx);
+        resultSubroutines.remove(idx);
+
+        for (int i = 0; i < sz; i++) {
+            int label = successors.get(i);
+            if (label >= minLabel) {
+                idx = labelToResultIndex(label);
+                if (idx < 0) {
+                    throw new RuntimeException("Invalid label "
+                            + Hex.u2(label));
+                }
+                removeBlockAndSpecialSuccessors(idx);
+            }
+        }
+    }
+
+    /**
+     * Extracts the resulting {@link RopMethod} from the instance.
+     * 
+     * @return non-null; the method object
+     */
+    private RopMethod getRopMethod() {
+
+        // Construct the final list of blocks.
+
+        int sz = result.size();
+        BasicBlockList bbl = new BasicBlockList(sz);
+        for (int i = 0; i < sz; i++) {
+            bbl.set(i, result.get(i));
+        }
+        bbl.setImmutable();
+
+        // Construct the method object to wrap it all up.
+
+        /*
+         * Note: The parameter assignment block is always the first
+         * that should be executed, hence the second argument to the
+         * constructor.
+         */
+        return new RopMethod(bbl, getSpecialLabel(PARAM_ASSIGNMENT));
+    }
+
+    /**
+     * Does the conversion.
+     */
+    private void doit() {
+        int[] workSet = Bits.makeBitSet(maxLabel);
+
+        Bits.set(workSet, 0);
+        addSetupBlocks();
+        setFirstFrame();
+
+        for (;;) {
+            int offset = Bits.findFirst(workSet, 0);
+            if (offset < 0) {
+                break;
+            }
+            Bits.clear(workSet, offset);
+            ByteBlock block = blocks.labelToBlock(offset);
+            Frame frame = startFrames[offset];
+            try {
+                processBlock(block, frame, workSet);
+            } catch (SimException ex) {
+                ex.addContext("...while working on block " + Hex.u2(offset));
+                throw ex;
+            }
+        }
+
+        addReturnBlock();
+        addSynchExceptionHandlerBlock();
+        addExceptionSetupBlocks();
+
+        if (hasSubroutines) {
+            // Subroutines are very rare, so skip this step if it's n/a
+            inlineSubroutines();
+        }
+    }
+
+    /**
+     * Sets up the first frame to contain all the incoming parameters in
+     * locals.
+     */
+    private void setFirstFrame() {
+        Prototype desc = method.getEffectiveDescriptor();
+        startFrames[0].initializeWithParameters(desc.getParameterTypes());
+        startFrames[0].setImmutable();
+    }
+
+    /**
+     * Processes the given block.
+     * 
+     * @param block non-null; block to process
+     * @param frame non-null; start frame for the block
+     * @param workSet non-null; bits representing work to do, which this
+     * method may add to
+     */
+    private void processBlock(ByteBlock block, Frame frame, int[] workSet) {
+        // Prepare the list of caught exceptions for this block.
+        ByteCatchList catches = block.getCatches();
+        machine.startBlock(catches.toRopCatchList());
+
+        /*
+         * Using a copy of the given frame, simulate each instruction,
+         * calling into machine for each.
+         */
+        frame = frame.copy();
+        sim.simulate(block, frame);
+        frame.setImmutable();
+
+        int extraBlockCount = machine.getExtraBlockCount();
+        ArrayList<Insn> insns = machine.getInsns();
+        int insnSz = insns.size();
+
+        /*
+         * Merge the frame into each possible non-exceptional
+         * successor.
+         */
+
+        int catchSz = catches.size();
+        IntList successors = block.getSuccessors();
+
+        int startSuccessorIndex;
+
+        Subroutine calledSubroutine = null;
+        if (machine.hasJsr()) {
+            /*
+             * If this frame ends in a JSR, only merge our frame with
+             * the subroutine start, not the subroutine's return target.
+             */
+            startSuccessorIndex = 1;
+
+            int subroutineLabel = successors.get(1);
+
+            if (subroutines[subroutineLabel] == null) {
+                subroutines[subroutineLabel] = new Subroutine (subroutineLabel);
+            }
+            
+            subroutines[subroutineLabel].addCallerBlock(block.getLabel());
+
+            calledSubroutine = subroutines[subroutineLabel];
+        } else if (machine.hasRet()) {
+            /*
+             * This block ends in a ret, which means it's the final block
+             * in some subroutine. Ultimately, this block will be copied
+             * and inlined for each call and then disposed of.
+             */
+
+            ReturnAddress ra = machine.getReturnAddress();
+            int subroutineLabel = ra.getSubroutineAddress();
+
+            if (subroutines[subroutineLabel] == null) {
+                subroutines[subroutineLabel]
+                        = new Subroutine (subroutineLabel, block.getLabel());
+            } else {
+                subroutines[subroutineLabel].addRetBlock(block.getLabel());
+            }
+
+            successors = subroutines[subroutineLabel].getSuccessors();
+            subroutines[subroutineLabel]
+                    .mergeToSuccessors(frame, workSet);
+            // Skip processing below since we just did it.
+            startSuccessorIndex = successors.size();
+        } else if (machine.wereCatchesUsed()) {
+            /*
+             * If there are catches, then the first successors
+             * (which will either be all of them or all but the last one)
+             * are catch targets.
+             */
+            startSuccessorIndex = catchSz;
+        } else {
+            startSuccessorIndex = 0;
+        }
+
+        int succSz = successors.size();
+        for (int i = startSuccessorIndex; i < succSz;
+             i++) {
+            int succ = successors.get(i);
+            try {
+                mergeAndWorkAsNecessary(succ, block.getLabel(),
+                        calledSubroutine, frame, workSet);
+            } catch (SimException ex) {
+                ex.addContext("...while merging to block " + Hex.u2(succ));
+                throw ex;
+            }
+        }
+
+        if ((succSz == 0) && machine.returns()) {
+            /*
+             * The block originally contained a return, but it has
+             * been made to instead end with a goto, and we need to
+             * tell it at this point that its sole successor is the
+             * return block. This has to happen after the merge loop
+             * above, since, at this point, the return block doesn't
+             * actually exist; it gets synthesized at the end of
+             * processing the original blocks.
+             */
+            successors = IntList.makeImmutable(getSpecialLabel(RETURN));
+            succSz = 1;
+        }
+
+        int primarySucc;
+
+        if (succSz == 0) {
+            primarySucc = -1;
+        } else {
+            primarySucc = machine.getPrimarySuccessorIndex();
+            if (primarySucc >= 0) {
+                primarySucc = successors.get(primarySucc);
+            }
+        }
+
+        /*
+         * This variable is true only when the method is synchronized and
+         * the block being processed can possibly throw an exception.
+         */
+        boolean synch = isSynchronized() && machine.canThrow();
+
+        if (synch || (catchSz != 0)) {
+            /*
+             * Deal with exception handlers: Merge an exception-catch
+             * frame into each possible exception handler, and
+             * construct a new set of successors to point at the
+             * exception handler setup blocks (which get synthesized
+             * at the very end of processing).
+             */
+            boolean catchesAny = false;
+            IntList newSucc = new IntList(succSz);
+            for (int i = 0; i < catchSz; i++) {
+                ByteCatchList.Item one = catches.get(i);
+                CstType exceptionClass = one.getExceptionClass();
+                int targ = one.getHandlerPc();
+
+                catchesAny |= (exceptionClass == CstType.OBJECT);
+
+                Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass);
+
+                try {
+                    mergeAndWorkAsNecessary(targ, block.getLabel(),
+                            null, f, workSet);
+                } catch (SimException ex) {
+                    ex.addContext("...while merging exception to block " +
+                                  Hex.u2(targ));
+                    throw ex;
+                }
+
+                /*
+                 * Set up the exception handler type, by setting it if
+                 * the given handler has yet to be encountered, or by
+                 * conservatively unioning if it has.
+                 */
+                Type already = catchTypes[targ];
+                if (already == null) {
+                    catchTypes[targ] = exceptionClass.getClassType();
+                } else if (already != exceptionClass.getClassType()) {
+                    catchTypes[targ] = Type.OBJECT;
+                }
+
+                /*
+                 * The synthesized exception setup block will have the
+                 * label getExceptionSetupLabel(targ).
+                 */
+                newSucc.add(getExceptionSetupLabel(targ));
+            }
+
+            if (synch && !catchesAny) {
+                /*
+                 * The method is synchronized and this block doesn't
+                 * already have a catch-all handler, so add one to the
+                 * end, both in the successors and in the throwing
+                 * instruction(s) at the end of the block (which is where
+                 * the caught classes live).
+                 */
+                newSucc.add(getSpecialLabel(SYNCH_CATCH_1));
+                synchNeedsExceptionHandler = true;
+
+                for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) {
+                    Insn insn = insns.get(i);
+                    if (insn.canThrow()) {
+                        insn = insn.withAddedCatch(Type.OBJECT);
+                        insns.set(i, insn);
+                    }
+                }
+            }
+
+            if (primarySucc >= 0) {
+                newSucc.add(primarySucc);
+            }
+
+            newSucc.setImmutable();
+            successors = newSucc;
+        }
+
+        // Construct the final resulting block(s), and store it (them).
+
+        int primarySuccListIndex = successors.indexOf(primarySucc);
+
+        /*
+         * If there are any extra blocks, work backwards through the
+         * list of instructions, adding single-instruction blocks, and
+         * resetting the successors variables as appropriate.
+         */
+        for (/*extraBlockCount*/; extraBlockCount > 0; extraBlockCount--) {
+            /*
+             * Some of the blocks that the RopperMachine wants added
+             * are for move-result insns, and these need goto insns as well.
+             */
+            Insn extraInsn = insns.get(--insnSz);
+            boolean needsGoto
+                    = extraInsn.getOpcode().getBranchingness()
+                        == Rop.BRANCH_NONE;
+            InsnList il = new InsnList(needsGoto ? 2 : 1);
+            IntList extraBlockSuccessors = successors;
+
+            il.set(0, extraInsn);
+
+            if (needsGoto) {
+                il.set(1, new PlainInsn(Rops.GOTO,
+                        extraInsn.getPosition(), null,
+                        RegisterSpecList.EMPTY));
+                /*
+                 * Obviously, this block won't be throwing an exception
+                 * so it should only have one successor.
+                 */
+                extraBlockSuccessors = IntList.makeImmutable(primarySucc);
+            }
+            il.setImmutable();
+
+            int label = getAvailableLabel();
+            BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors,
+                    primarySucc);
+            // All of these extra blocks will be in the same subroutine
+            addBlock(bb, frame.getSubroutines());
+
+            successors = successors.mutableCopy();
+            successors.set(primarySuccListIndex, label);
+            successors.setImmutable();
+            primarySucc = label;
+        }
+        
+        Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1);
+
+        /*
+         * Add a goto to the end of the block if it doesn't already
+         * end with a branch, to maintain the invariant that all
+         * blocks end with a branch of some sort or other. Note that
+         * it is possible for there to be blocks for which no
+         * instructions were ever output (e.g., only consist of pop*
+         * in the original Java bytecode).
+         */
+        if ((lastInsn == null) ||
+            (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) {
+            SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO :
+                lastInsn.getPosition();
+            insns.add(new PlainInsn(Rops.GOTO, pos, null,
+                                    RegisterSpecList.EMPTY));
+            insnSz++;
+        }
+        
+        /*
+         * Construct a block for the remaining instructions (which in
+         * the usual case is all of them).
+         */
+
+        InsnList il = new InsnList(insnSz);
+        for (int i = 0; i < insnSz; i++) {
+            il.set(i, insns.get(i));
+        }
+        il.setImmutable();
+
+        BasicBlock bb =
+            new BasicBlock(block.getLabel(), il, successors, primarySucc);
+        addOrReplaceBlock(bb, frame.getSubroutines());
+    }
+
+    /**
+     * Helper for {@link #processBlock}, which merges frames and
+     * adds to the work set, as necessary.
+     * 
+     * @param label &gt;= 0; label to work on
+     * @param pred  predecessor label. Must be &gt;= 0 when
+     * <code>label</code> is a subroutine start block and calledSubroutine
+     * is non-null. Otherwise, may be -1.
+     * @param calledSubroutine null-ok; a Subroutine instance if
+     * <code>label</code> is the first block in a subroutine.
+     * @param frame non-null; new frame for the labelled block
+     * @param workSet non-null; bits representing work to do, which this
+     * method may add to
+     */
+    private void mergeAndWorkAsNecessary(int label, int pred,
+            Subroutine calledSubroutine, Frame frame, int[] workSet) {
+        Frame existing = startFrames[label];
+        Frame merged;
+
+        if (existing != null) {
+            /*
+             * Some other block also continues at this label. Merge
+             * the frames, and re-set the bit in the work set if there
+             * was a change.
+             */
+            if (calledSubroutine != null) {
+                merged = existing.mergeWithSubroutineCaller(frame,
+                        calledSubroutine.getStartBlock(), pred);
+            } else {
+                merged = existing.mergeWith(frame);
+            }
+            if (merged != existing) {
+                startFrames[label] = merged;
+                Bits.set(workSet, label);
+            }
+        } else {
+            // This is the first time this label has been encountered.
+            if (calledSubroutine != null) {
+                startFrames[label]
+                        = frame.makeNewSubroutineStartFrame(label, pred);
+            } else {
+                startFrames[label] = frame;
+            }
+            Bits.set(workSet, label);
+        }
+    }
+
+    /**
+     * Constructs and adds the blocks that perform setup for the rest of
+     * the method. This includes a first block which merely contains
+     * assignments from parameters to the same-numbered registers and
+     * a possible second block which deals with synchronization.
+     */
+    private void addSetupBlocks() {
+        LocalVariableList localVariables = method.getLocalVariables();
+        SourcePosition pos = method.makeSourcePosistion(0);
+        Prototype desc = method.getEffectiveDescriptor();
+        StdTypeList params = desc.getParameterTypes();
+        int sz = params.size();
+        InsnList insns = new InsnList(sz + 1);
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Type one = params.get(i);
+            LocalVariableList.Item local = localVariables.pcAndIndexToLocal(0, at);
+            RegisterSpec result = (local == null) ?
+                RegisterSpec.make(at, one) :
+                RegisterSpec.makeLocalOptional(at, one, local.getLocalItem());
+
+            Insn insn = new PlainCstInsn(Rops.opMoveParam(one), pos, result,
+                                         RegisterSpecList.EMPTY,
+                                         CstInteger.make(at));
+            insns.set(i, insn);
+            at += one.getCategory();
+        }
+
+        insns.set(sz, new PlainInsn(Rops.GOTO, pos, null, 
+                                    RegisterSpecList.EMPTY));
+        insns.setImmutable();
+
+        boolean synch = isSynchronized();
+        int label = synch ? getSpecialLabel(SYNCH_SETUP_1) : 0;
+        BasicBlock bb =
+            new BasicBlock(getSpecialLabel(PARAM_ASSIGNMENT), insns,
+                           IntList.makeImmutable(label), label);
+        addBlock(bb, IntList.EMPTY);
+
+        if (synch) {
+            RegisterSpec synchReg = getSynchReg();
+            Insn insn;
+            if (isStatic()) {
+                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+                                           RegisterSpecList.EMPTY,
+                                           StdTypeList.EMPTY,
+                                           method.getDefiningClass());
+                insns = new InsnList(1);
+                insns.set(0, insn);
+            } else {
+                insns = new InsnList(2);
+                insn = new PlainCstInsn(Rops.MOVE_PARAM_OBJECT, pos,
+                                        synchReg, RegisterSpecList.EMPTY,
+                                        CstInteger.VALUE_0);
+                insns.set(0, insn);
+                insns.set(1, new PlainInsn(Rops.GOTO, pos, null,
+                                           RegisterSpecList.EMPTY));
+            }
+
+            int label2 = getSpecialLabel(SYNCH_SETUP_2);
+            insns.setImmutable();
+            bb = new BasicBlock(label, insns,
+                                IntList.makeImmutable(label2), label2);
+            addBlock(bb, IntList.EMPTY);
+
+            insns = new InsnList(isStatic() ? 2 : 1);
+
+            if (isStatic()) {
+                insns.set(0, new PlainInsn(Rops.opMoveResultPseudo(synchReg),
+                        pos, synchReg, RegisterSpecList.EMPTY));
+            }
+
+            insn = new ThrowingInsn(Rops.MONITOR_ENTER, pos,
+                                    RegisterSpecList.make(synchReg),
+                                    StdTypeList.EMPTY);
+            insns.set(isStatic() ? 1 :0, insn);
+            insns.setImmutable();
+            bb = new BasicBlock(label2, insns, IntList.makeImmutable(0), 0);
+            addBlock(bb, IntList.EMPTY);
+        }
+    }
+
+    /**
+     * Constructs and adds the return block, if necessary. The return
+     * block merely contains an appropriate <code>return</code>
+     * instruction.
+     */
+    private void addReturnBlock() {
+        Rop returnOp = machine.getReturnOp();
+
+        if (returnOp == null) {
+            /*
+             * The method being converted never returns normally, so there's
+             * no need for a return block.
+             */
+            return;
+        }
+
+        SourcePosition returnPos = machine.getReturnPosition();
+        int label = getSpecialLabel(RETURN);
+
+        if (isSynchronized()) {
+            InsnList insns = new InsnList(1);
+            Insn insn = new ThrowingInsn(Rops.MONITOR_EXIT, returnPos,
+                                         RegisterSpecList.make(getSynchReg()),
+                                         StdTypeList.EMPTY);
+            insns.set(0, insn);
+            insns.setImmutable();
+
+            int nextLabel = getSpecialLabel(SYNCH_RETURN);
+            BasicBlock bb =
+                new BasicBlock(label, insns,
+                               IntList.makeImmutable(nextLabel), nextLabel);
+            addBlock(bb, IntList.EMPTY);
+
+            label = nextLabel;
+        }
+
+        InsnList insns = new InsnList(1);
+        TypeList sourceTypes = returnOp.getSources();
+        RegisterSpecList sources;
+
+        if (sourceTypes.size() == 0) {
+            sources = RegisterSpecList.EMPTY;
+        } else {
+            RegisterSpec source = RegisterSpec.make(0, sourceTypes.getType(0));
+            sources = RegisterSpecList.make(source);
+        }
+
+        Insn insn = new PlainInsn(returnOp, returnPos, null, sources);
+        insns.set(0, insn);
+        insns.setImmutable();
+
+        BasicBlock bb = new BasicBlock(label, insns, IntList.EMPTY, -1);
+        addBlock(bb, IntList.EMPTY);
+    }
+
+    /**
+     * Constructs and adds, if necessary, the catch-all exception handler
+     * block to deal with unwinding the lock taken on entry to a synchronized
+     * method.
+     */
+    private void addSynchExceptionHandlerBlock() {
+        if (!synchNeedsExceptionHandler) {
+            /*
+             * The method being converted either isn't synchronized or
+             * can't possibly throw exceptions in its main body, so
+             * there's no need for a synchronized method exception
+             * handler.
+             */
+            return;
+        }
+
+        SourcePosition pos = method.makeSourcePosistion(0);
+        RegisterSpec exReg = RegisterSpec.make(0, Type.THROWABLE);
+        BasicBlock bb;
+        Insn insn;
+
+        InsnList insns = new InsnList(2);
+        insn = new PlainInsn(Rops.opMoveException(Type.THROWABLE), pos,
+                             exReg, RegisterSpecList.EMPTY);
+        insns.set(0, insn);
+        insn = new ThrowingInsn(Rops.MONITOR_EXIT, pos,
+                                RegisterSpecList.make(getSynchReg()),
+                                StdTypeList.EMPTY);
+        insns.set(1, insn);
+        insns.setImmutable();
+
+        int label2 = getSpecialLabel(SYNCH_CATCH_2);
+        bb = new BasicBlock(getSpecialLabel(SYNCH_CATCH_1), insns,
+                            IntList.makeImmutable(label2), label2);
+        addBlock(bb, IntList.EMPTY);
+
+        insns = new InsnList(1);
+        insn = new ThrowingInsn(Rops.THROW, pos,
+                                RegisterSpecList.make(exReg),
+                                StdTypeList.EMPTY);
+        insns.set(0, insn);
+        insns.setImmutable();
+
+        bb = new BasicBlock(label2, insns, IntList.EMPTY, -1);
+        addBlock(bb, IntList.EMPTY);
+    }
+
+    /**
+     * Creates the exception handler setup blocks. "maxLocals"
+     * below is because that's the register number corresponding
+     * to the sole element on a one-deep stack (which is the
+     * situation at the start of an exception handler block).
+     */
+    private void addExceptionSetupBlocks() {
+
+        int len = catchTypes.length;
+        for (int i = 0; i < len; i++) {
+            Type one = catchTypes[i];
+            if (one != null) {
+                Insn proto = labelToBlock(i).getFirstInsn();
+                SourcePosition pos = proto.getPosition();
+                InsnList il = new InsnList(2);
+
+                Insn insn = new PlainInsn(Rops.opMoveException(one),
+                                          pos,
+                                          RegisterSpec.make(maxLocals, one),
+                                          RegisterSpecList.EMPTY);
+                il.set(0, insn);
+
+                insn = new PlainInsn(Rops.GOTO, pos, null,
+                                     RegisterSpecList.EMPTY);
+                il.set(1, insn);
+                il.setImmutable();
+
+                BasicBlock bb = new BasicBlock(getExceptionSetupLabel(i),
+                                               il,
+                                               IntList.makeImmutable(i),
+                                               i);
+                addBlock(bb, startFrames[i].getSubroutines());
+            }
+        }
+    }
+
+    /**
+     * Checks to see if the basic block is a subroutine caller block.
+     *
+     * @param bb non-null; the basic block in question
+     * @return true if this block calls a subroutine
+     */
+    private boolean isSubroutineCaller(BasicBlock bb) {
+        IntList successors = bb.getSuccessors();
+        if (successors.size() < 2) return false;
+
+        int subLabel = successors.get(1);
+
+        return (subLabel < subroutines.length)
+                && (subroutines[subLabel] != null);
+    }
+
+    /**
+     * Inlines any subroutine calls
+     */
+    private void inlineSubroutines() {
+        final IntList reachableSubroutineCallerLabels = new IntList(4);
+
+        /*
+         * Compile a list of all subroutine calls reachable
+         * through the normal (non-subroutine) flow.  We do this first, since
+         * we'll be affecting the call flow as we go.
+         * 
+         * Start at label 0 --  the param assignment block has nothing for us
+         */
+        forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() {
+            public void visitBlock(BasicBlock b) {
+                if (isSubroutineCaller(b)) {
+                    reachableSubroutineCallerLabels.add(b.getLabel());
+                }
+            }
+        });
+
+        /*
+         * Convert the resultSubroutines list, indexed by block index,
+         * to a label-to-subroutines mapping used by the inliner.
+         */
+        int largestAllocedLabel = getAvailableLabel();
+        ArrayList<IntList> labelToSubroutines
+                = new ArrayList<IntList>(largestAllocedLabel);
+        for (int i = 0; i < largestAllocedLabel; i++) {
+            labelToSubroutines.add(null);
+        }
+
+        for (int i = 0; i < result.size(); i++) {
+            BasicBlock b = result.get(i);
+            if (b == null) {
+                continue;
+            }
+            IntList subroutineList = resultSubroutines.get(i);
+            labelToSubroutines.set(b.getLabel(), subroutineList);
+        }
+
+        /*
+        * Inline all reachable subroutines.
+        * Inner subroutines will be inlined as they are encountered.
+        */
+        int sz = reachableSubroutineCallerLabels.size();
+        for (int i = 0 ; i < sz ; i++) {
+            int label = reachableSubroutineCallerLabels.get(i);
+            new SubroutineInliner(
+                    new LabelAllocator(getAvailableLabel()), labelToSubroutines)
+                    .inlineSubroutineCalledFrom(labelToBlock(label));
+        }
+
+        // Now find the blocks that aren't reachable and remove them
+        deleteUnreachableBlocks();
+    }
+
+    /**
+     * Deletes all blocks that cannot be reached. This is run to delete
+     * original subroutine blocks after subroutine inlining.
+     */
+    private void deleteUnreachableBlocks() {
+        final IntList reachableLabels = new IntList(result.size());
+
+        // subroutine inlining is done now and we won't update this list here
+        resultSubroutines.clear();
+
+        forEachNonSubBlockDepthFirst(getSpecialLabel(PARAM_ASSIGNMENT),
+                new BasicBlock.Visitor() {
+
+            public void visitBlock(BasicBlock b) {
+                reachableLabels.add(b.getLabel());
+            }
+        });
+
+        reachableLabels.sort();
+
+        for (int i = result.size() - 1 ; i >= 0 ; i--) {
+            if (reachableLabels.indexOf(result.get(i).getLabel()) < 0) {
+                result.remove(i);
+                // unnecessary here really, since subroutine inlining is done
+                //resultSubroutines.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Allocates labels, without requiring previously allocated labels
+     * to have been added to the blocks list.
+     */
+    private static class LabelAllocator {
+        int nextAvailableLabel;
+
+        /**
+         * @param startLabel available label to start allocating from
+         */
+        LabelAllocator(int startLabel) {
+            nextAvailableLabel = startLabel;
+        }
+
+        /**
+         * @return next available label
+         */
+        int getNextLabel() {
+            return nextAvailableLabel++;
+        }
+    }
+
+    /**
+     * Inlines a subroutine. Start by calling
+     * <code>inlineSubroutineCalledFrom</code>.
+     */
+    private class SubroutineInliner {
+        /**
+         * maps original label to the label
+         * that will be used by the inlined version
+         */
+        private final HashMap<Integer, Integer> origLabelToCopiedLabel;
+
+        /** Set of original labels that need to be copied. */
+        private final BitSet workList;
+
+        /** The label of the original start block for this subroutine. */
+        private int subroutineStart;
+
+        /** The label of the ultimate return block. */
+        private int subroutineSuccessor;
+
+        /** Used for generating new labels for copied blocks. */
+        private final LabelAllocator labelAllocator;
+
+        /**
+         * A mapping, indexed by label, to subroutine nesting list.
+         * The subroutine nest list is as returned by
+         * {@link Frame#getSubroutines}.
+         */
+        private final ArrayList<IntList> labelToSubroutines;
+
+        SubroutineInliner(final LabelAllocator labelAllocator,
+                ArrayList<IntList> labelToSubroutines) {
+            origLabelToCopiedLabel = new HashMap<Integer, Integer>();
+
+            workList = new BitSet(maxLabel);
+
+            this.labelAllocator = labelAllocator;
+            this.labelToSubroutines = labelToSubroutines;
+        }
+
+        /**
+         * Inlines a subroutine.
+         * 
+         * @param b block where jsr occurred in the original bytecode
+         */
+        void inlineSubroutineCalledFrom(final BasicBlock b) {
+
+            /*
+             * The 0th successor of a subroutine caller block is where
+             * the subroutine should return to. The 1st successor is
+             * the start block of the subroutine.
+             */
+            subroutineSuccessor = b.getSuccessors().get(0);
+            subroutineStart = b.getSuccessors().get(1);
+
+            /*
+             * This allocates an initial label and adds the first
+             * block to the worklist.
+             */
+            int newSubStartLabel = mapOrAllocateLabel(subroutineStart);
+
+            for(int label = workList.nextSetBit(0); label >= 0
+                    ; label = workList.nextSetBit(0)) {
+
+                workList.clear(label);
+                int newLabel = origLabelToCopiedLabel.get(label);
+
+                copyBlock(label, newLabel);
+
+                if (isSubroutineCaller(labelToBlock(label))) {
+                    new SubroutineInliner(labelAllocator, labelToSubroutines)
+                        .inlineSubroutineCalledFrom(labelToBlock(newLabel));
+                }
+            }
+
+            /*
+             * Replace the original caller block, since we now have a
+             * new successor
+             */
+
+            addOrReplaceBlockNoDelete(
+                new BasicBlock(b.getLabel(), b.getInsns(),
+                    IntList.makeImmutable (newSubStartLabel),
+                            newSubStartLabel), labelToSubroutines.get(b.getLabel()));
+       }
+
+        /**
+         * Copies a basic block, mapping its successors along the way.
+         * @param origLabel original block label
+         * @param newLabel label that the new block should have
+         */
+       private void copyBlock(int origLabel, int newLabel) {
+
+            BasicBlock origBlock = labelToBlock(origLabel);
+
+            final IntList origSuccessors = origBlock.getSuccessors();
+            IntList successors;
+            int primarySuccessor = -1;
+            Subroutine subroutine;
+
+            if (isSubroutineCaller(origBlock)) {
+                /*
+                 * A subroutine call inside a subroutine call.
+                 * Set up so we can recurse. The caller block should have
+                 * it's first successor be a copied block that will be
+                 * the subroutine's return point. It's second successor will
+                 * be copied when we recurse, and remains as the original
+                 * label of the start of the inner subroutine.
+                 */
+
+                successors = IntList.makeImmutable(
+                        mapOrAllocateLabel(origSuccessors.get(0)),
+                        origSuccessors.get(1));
+                // primary successor will be set when this block is replaced
+            } else if (null
+                    != (subroutine = subroutineFromRetBlock(origLabel))) {
+                /*
+                 * this is a ret block -- its successor
+                 * should be subroutineSuccessor
+                 */
+
+                // Sanity check
+                if (subroutine.startBlock != subroutineStart) {
+                    throw new RuntimeException (
+                            "ret instruction returns to label "
+                            + Hex.u2 (subroutine.startBlock)
+                            + " expected: " + Hex.u2(subroutineStart));
+                }
+
+                successors = IntList.makeImmutable(subroutineSuccessor);
+                primarySuccessor = subroutineSuccessor;
+            } else {
+                // Map all the successor labels
+
+                int origPrimary = origBlock.getPrimarySuccessor();
+                int sz = origSuccessors.size();
+
+                successors = new IntList(sz);
+
+                for (int i = 0 ; i < sz ; i++) {
+                    int origSuccLabel = origSuccessors.get(i);
+                    int newSuccLabel =  mapOrAllocateLabel(origSuccLabel);
+
+                    successors.add(newSuccLabel);
+
+                    if (origPrimary == origSuccLabel) {
+                        primarySuccessor = newSuccLabel;
+                    }
+                }
+
+                successors.setImmutable();
+            }
+
+            addBlock (
+                new BasicBlock(newLabel,
+                    filterMoveReturnAddressInsns(origBlock.getInsns()),
+                    successors, primarySuccessor),
+                    labelToSubroutines.get(newLabel));
+        }
+
+        /**
+         * Checks to see if a specified label is involved in a specified
+         * subroutine.
+         *
+         * @param label &gt;=0 a basic block label
+         * @param subroutineStart &gt;=0 a subroutine as identified by the
+         * label of its start block.
+         * @return true if the block is dominated by the subroutine call.
+         */
+        private boolean involvedInSubroutine(int label, int subroutineStart) {
+            IntList subroutinesList = labelToSubroutines.get(label);
+            return (subroutinesList.size() > 0
+                    && subroutinesList.top() == subroutineStart);
+        }
+
+        /**
+         * Maps the label of a pre-copied block to the label of the inlined
+         * block, allocating a new label and adding it to the worklist
+         * if necessary.  If the origLabel is a "special" label, it
+         * is returned exactly and not scheduled for duplication: copying
+         * never proceeds past a special label, which likely is the function
+         * return block or an immediate predecessor.
+         *
+         * @param origLabel label of original, pre-copied block
+         * @return label for new, inlined block
+         */
+        private int mapOrAllocateLabel(int origLabel) {
+            int resultLabel;
+            Integer mappedLabel = origLabelToCopiedLabel.get(origLabel);
+
+            if (mappedLabel != null) {
+                resultLabel = mappedLabel;
+            } else if (!involvedInSubroutine(origLabel,subroutineStart)) {
+                /*
+                 * A subroutine has ended by some means other than a "ret"
+                 * (which really means a throw caught later).
+                 */
+                resultLabel = origLabel;
+            } else {
+                resultLabel = labelAllocator.getNextLabel();
+                workList.set(origLabel);
+                origLabelToCopiedLabel.put(origLabel, resultLabel);
+
+                // The new label has the same frame as the original label
+                while (labelToSubroutines.size() <= resultLabel) {
+                    labelToSubroutines.add(null);
+                }
+                labelToSubroutines.set(resultLabel,
+                        labelToSubroutines.get(origLabel));
+            }
+
+            return resultLabel;
+        }
+    }
+
+    /**
+     * Finds a <code>Subroutine<code> that is returned from by a ret in
+     * a given block.
+     * @param label A block that originally contained a ret instruction
+     * @return null-ok; Subroutine or null if none was found.
+     */
+    private Subroutine subroutineFromRetBlock(int label) {
+        for (int i = subroutines.length - 1 ; i >= 0 ; i--) {
+            if (subroutines[i] != null) {
+                Subroutine subroutine = subroutines[i];
+
+                if (subroutine.retBlocks.get(label)) {
+                    return subroutine;
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Removes all move-return-address instructions, returning a new InsnList
+     * if necessary.  The move-return-address insns are dead code after
+     * subroutines have been inlined.
+     *
+     * @param insns InsnList that may contain move-return-address insns
+     * @return InsnList with move-return-address removed.
+     */
+    private InsnList filterMoveReturnAddressInsns(InsnList insns) {
+        int sz;
+        int newSz = 0;
+
+        // First see if we need to filter, and if so what the new size will be
+        sz = insns.size();
+        for (int i = 0; i < sz; i++) {
+            if (insns.get(i).getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+                newSz++;
+            }
+        }
+
+        if (newSz == sz) {
+            return insns;
+        }
+
+        // Make a new list without the MOVE_RETURN_ADDRESS insns
+        InsnList newInsns = new InsnList(newSz);
+
+        int newIndex = 0;
+        for (int i = 0; i < sz; i++) {
+            Insn insn = insns.get(i);
+            if (insn.getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+                newInsns.set(newIndex++, insn);
+            }
+        }
+
+        newInsns.setImmutable();
+        return newInsns;
+    }
+
+    /**
+     * Visits each non-subroutine block once in depth-first successor order.
+     * @param firstLabel label of start block
+     * @param v callback interface
+     */
+    private void forEachNonSubBlockDepthFirst(
+            int firstLabel, BasicBlock.Visitor v) {
+
+        forEachNonSubBlockDepthFirst0(labelToBlock(firstLabel),
+                v, new BitSet(maxLabel));
+    }
+
+    /**
+     * Visits each block once in depth-first successor order, ignoring jsr
+     * targets.  Worker for forEachNonSubBlockDepthFirst().
+     * @param next next block to visit
+     * @param v callback interface
+     * @param visited set of blocks already visited
+     */
+    private void forEachNonSubBlockDepthFirst0(
+            BasicBlock next, BasicBlock.Visitor v, BitSet visited) {
+
+        v.visitBlock(next);
+        visited.set(next.getLabel());
+
+        IntList successors = next.getSuccessors();
+
+        int sz = successors.size();
+
+        for (int i = 0 ; i < sz ; i++) {
+            int succ = successors.get(i);
+
+            if (visited.get(succ)) {
+                continue;
+            }
+
+            if (isSubroutineCaller(next) && i > 0) {
+                // ignore jsr targets
+                continue;
+            }
+
+            /*
+             * Ignore missing labels: they're successors of
+             * subroutines that never invoke a ret.
+             */
+            int idx = labelToResultIndex(succ);
+            if (idx >= 0) {
+                forEachNonSubBlockDepthFirst0(result.get(idx), v, visited);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/RopperMachine.java b/dx/src/com/android/dx/cf/code/RopperMachine.java
new file mode 100644
index 0000000..6d05b38
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/RopperMachine.java
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Machine implementation for use by {@link Ropper}.
+ */
+/*package*/ final class RopperMachine extends ValueAwareMachine {
+    /** non-null; array reflection class */
+    private static final CstType ARRAY_REFLECT_TYPE =
+        new CstType(Type.internClassName("java/lang/reflect/Array"));
+
+    /**
+     * non-null; method constant for use in converting
+     * <code>multianewarray</code> instructions 
+     */
+    private static final CstMethodRef MULTIANEWARRAY_METHOD =
+        new CstMethodRef(ARRAY_REFLECT_TYPE,
+                         new CstNat(new CstUtf8("newInstance"),
+                                    new CstUtf8("(Ljava/lang/Class;[I)" +
+                                                "Ljava/lang/Object;")));
+
+    /** non-null; {@link Ropper} controlling this instance */
+    private final Ropper ropper;
+
+    /** non-null; method being converted */
+    private final ConcreteMethod method;
+
+    /** non-null; translation advice */
+    private final TranslationAdvice advice;
+
+    /** max locals of the method */
+    private final int maxLocals;
+
+    /** non-null; instructions for the rop basic block in-progress */
+    private final ArrayList<Insn> insns;
+
+    /** non-null; catches for the block currently being processed */
+    private TypeList catches;
+
+    /** whether the catches have been used in an instruction */
+    private boolean catchesUsed;
+
+    /** whether the block contains a <code>return</code> */
+    private boolean returns;
+
+    /** primary successor index */
+    private int primarySuccessorIndex;
+
+    /** &gt;= 0; number of extra basic blocks required */
+    private int extraBlockCount;
+
+    /** true if last processed block ends with a jsr or jsr_W*/
+    private boolean hasJsr;
+
+    /** true if an exception can be thrown by the last block processed */
+    private boolean blockCanThrow;
+
+    /**
+     * If non-null, the ReturnAddress that was used by the terminating ret
+     * instruction. If null, there was no ret instruction encountered.
+     */
+
+    private ReturnAddress returnAddress;
+
+    /**
+     * null-ok; the appropriate <code>return</code> op or <code>null</code>
+     * if it is not yet known 
+     */
+    private Rop returnOp;
+
+    /**
+     * null-ok; the source position for the return block or <code>null</code>
+     * if it is not yet known 
+     */
+    private SourcePosition returnPosition;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param ropper non-null; ropper controlling this instance
+     * @param method non-null; method being converted
+     * @param advice non-null; translation advice to use
+     */
+    public RopperMachine(Ropper ropper, ConcreteMethod method,
+            TranslationAdvice advice) {
+        super(method.getEffectiveDescriptor());
+        
+        if (ropper == null) {
+            throw new NullPointerException("ropper == null");
+        }
+
+        if (advice == null) {
+            throw new NullPointerException("advice == null");
+        }
+
+        this.ropper = ropper;
+        this.method = method;
+        this.advice = advice;
+        this.maxLocals = method.getMaxLocals();
+        this.insns = new ArrayList<Insn>(25);
+        this.catches = null;
+        this.catchesUsed = false;
+        this.returns = false;
+        this.primarySuccessorIndex = -1;
+        this.extraBlockCount = 0;
+        this.blockCanThrow = false;
+        this.returnOp = null;
+        this.returnPosition = null;
+    }
+
+    /**
+     * Gets the instructions array. It is shared and gets modified by
+     * subsequent calls to this instance.
+     * 
+     * @return non-null; the instructions array
+     */
+    public ArrayList<Insn> getInsns() {
+        return insns;
+    }
+
+    /**
+     * Gets the return opcode encountered, if any.
+     * 
+     * @return null-ok; the return opcode
+     */
+    public Rop getReturnOp() {
+        return returnOp;
+    }
+
+    /**
+     * Gets the return position, if known.
+     * 
+     * @return null-ok; the return position
+     */
+    public SourcePosition getReturnPosition() {
+        return returnPosition;
+    }
+
+    /**
+     * Gets ready to start working on a new block. This will clear the
+     * {@link #insns} list, set {@link #catches}, reset whether it has
+     * been used, reset whether the block contains a
+     * <code>return</code>, and reset {@link #primarySuccessorIndex}.
+     */
+    public void startBlock(TypeList catches) {
+        this.catches = catches;
+
+        insns.clear();
+        catchesUsed = false;
+        returns = false;
+        primarySuccessorIndex = 0;
+        extraBlockCount = 0;
+        blockCanThrow = false;
+        hasJsr = false;
+        returnAddress = null;
+    }
+
+    /**
+     * Gets whether {@link #catches} was used. This indicates that the
+     * last instruction in the block is one of the ones that can throw.
+     * 
+     * @return whether <code>catches</code> has been used
+     */
+    public boolean wereCatchesUsed() {
+        return catchesUsed;
+    }
+
+    /**
+     * Gets whether the block just processed ended with a
+     * <code>return</code>.
+     * 
+     * @return whether the block returns
+     */
+    public boolean returns() {
+        return returns;
+    }
+
+    /**
+     * Gets the primary successor index. This is the index into the
+     * successors list where the primary may be found or
+     * <code>-1</code> if there are successors but no primary
+     * successor. This may return something other than
+     * <code>-1</code> in the case of an instruction with no
+     * successors at all (primary or otherwise).
+     * 
+     * @return &gt;= -1; the primary successor index
+     */
+    public int getPrimarySuccessorIndex() {
+        return primarySuccessorIndex;
+    }
+
+    /**
+     * Gets how many extra blocks will be needed to represent the
+     * block currently being translated. Each extra block should consist
+     * of one instruction from the end of the original block.
+     * 
+     * @return &gt;= 0; the number of extra blocks needed
+     */
+    public int getExtraBlockCount() {
+        return extraBlockCount;
+    }
+
+    /**
+     * @return true if at least one of the insn processed since the last
+     * call to startBlock() can throw.
+     */
+    public boolean canThrow() {
+        return blockCanThrow;
+    }
+
+    /**
+     * @return true if a JSR has ben encountered since the last call to
+     * startBlock()
+     */
+    public boolean hasJsr() {
+        return hasJsr;
+    }
+
+    /**
+     * @return true if a RET has ben encountered since the last call to 
+     * startBlock()
+     */
+    public boolean hasRet() {
+        return returnAddress != null;
+    }
+
+    /**
+     * @return null-ok; return address of a ret instruction if encountered
+     * since last call to startBlock(). null if no ret instruction encountered.
+     */
+    public ReturnAddress getReturnAddress() {
+        return returnAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void run(Frame frame, int offset, int opcode) {
+        /*
+         * This is the stack pointer after the opcode's arguments have been
+         * popped.
+         */
+        int stackPointer = maxLocals + frame.getStack().size();
+
+        // The sources have to be retrieved before super.run() gets called.
+        RegisterSpecList sources = getSources(opcode, stackPointer);
+        int sourceCount = sources.size();
+
+        super.run(frame, offset, opcode);
+
+        SourcePosition pos = method.makeSourcePosistion(offset);
+        RegisterSpec localTarget = getLocalTarget();
+        int destCount = resultCount();
+        RegisterSpec dest;
+
+        if (destCount == 0) {
+            dest = null;
+            switch (opcode) {
+                case ByteOps.POP:
+                case ByteOps.POP2: {
+                    // These simply don't appear in the rop form.
+                    return;
+                }
+            }
+        } else if (localTarget != null) {
+            dest = localTarget;
+        } else if (destCount == 1) {
+            dest = RegisterSpec.make(stackPointer, result(0));
+        } else {
+            /*
+             * This clause only ever applies to the stack manipulation
+             * ops that have results (that is, dup* and swap but not
+             * pop*).
+             *
+             * What we do is first move all the source registers into
+             * the "temporary stack" area defined for the method, and
+             * then move stuff back down onto the main "stack" in the
+             * arrangement specified by the stack op pattern.
+             * 
+             * Note: This code ends up emitting a lot of what will
+             * turn out to be superfluous moves (e.g., moving back and
+             * forth to the same local when doing a dup); however,
+             * that makes this code a bit easier (and goodness knows
+             * it doesn't need any extra complexity), and all the SSA
+             * stuff is going to want to deal with this sort of
+             * superfluous assignment anyway, so it should be a wash
+             * in the end.
+             */
+            int scratchAt = ropper.getFirstTempStackReg();
+            RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
+
+            for (int i = 0; i < sourceCount; i++) {
+                RegisterSpec src = sources.get(i);
+                TypeBearer type = src.getTypeBearer();
+                RegisterSpec scratch = src.withReg(scratchAt);
+                insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
+                scratchRegs[i] = scratch;
+                scratchAt += src.getCategory();
+            }
+
+            for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+                int which = (pattern & 0x0f) - 1;
+                RegisterSpec scratch = scratchRegs[which];
+                TypeBearer type = scratch.getTypeBearer();
+                insns.add(new PlainInsn(Rops.opMove(type), pos,
+                                        scratch.withReg(stackPointer),
+                                        scratch));
+                stackPointer += type.getType().getCategory();
+            }
+            return;
+        }                
+
+        TypeBearer destType = (dest != null) ? dest : Type.VOID;
+        Constant cst = getAuxCst();
+        int ropOpcode;
+        Rop rop;
+        Insn insn;
+
+        if (opcode == ByteOps.MULTIANEWARRAY) {
+            blockCanThrow = true;
+
+            // Add the extra instructions for handling multianewarray.
+
+            extraBlockCount = 6;
+
+            /*
+             * Add an array constructor for the int[] containing all the
+             * dimensions.
+             */
+            RegisterSpec dimsReg = 
+                RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
+            rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
+            insn = new ThrowingCstInsn(rop, pos, sources, catches,
+                    CstType.INT_ARRAY);
+            insns.add(insn);
+
+            // Add a move-result for the new-filled-array
+            rop = Rops.opMoveResult(Type.INT_ARRAY);
+            insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * Add a const-class instruction for the specified array
+             * class. 
+             */
+
+            /*
+             * Remove as many dimensions from the originally specified
+             * class as are given in the explicit list of dimensions,
+             * so as to pass the right component class to the standard
+             * Java library array constructor.
+             */
+            Type componentType = ((CstType) cst).getClassType();
+            for (int i = 0; i < sourceCount; i++) {
+                componentType = componentType.getComponentType();
+            }
+
+            RegisterSpec classReg =
+                RegisterSpec.make(dest.getReg(), Type.CLASS);
+
+            if (componentType.isPrimitive()) {
+                /*
+                 * The component type is primitive (e.g., int as opposed
+                 * to Integer), so we have to fetch the corresponding
+                 * TYPE class.
+                 */
+                CstFieldRef typeField =
+                    CstFieldRef.forPrimitiveType(componentType);
+                insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos,
+                                           RegisterSpecList.EMPTY,
+                                           catches, typeField);
+            } else {
+                /*
+                 * The component type is an object type, so just make a
+                 * normal class reference.
+                 */
+                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+                                           RegisterSpecList.EMPTY, catches,
+                                           new CstType(componentType));
+            }
+
+            insns.add(insn);
+
+            // Add a move-result-pseudo for the get-static or const
+            rop = Rops.opMoveResultPseudo(classReg.getType());
+            insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * Add a call to the "multianewarray method," that is,
+             * Array.newInstance(class, dims). Note: The result type
+             * of newInstance() is Object, which is why the last
+             * instruction in this sequence is a cast to the right
+             * type for the original instruction.
+             */
+
+            RegisterSpec objectReg =
+                RegisterSpec.make(dest.getReg(), Type.OBJECT);
+            
+            insn = new ThrowingCstInsn(
+                    Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()),
+                    pos, RegisterSpecList.make(classReg, dimsReg),
+                    catches, MULTIANEWARRAY_METHOD);
+            insns.add(insn);
+
+            // Add a move-result
+            rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype()
+                    .getReturnType());
+            insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * And finally, set up for the remainder of this method to
+             * add an appropriate cast.
+             */
+
+            opcode = ByteOps.CHECKCAST;
+            sources = RegisterSpecList.make(objectReg);
+
+        } else if (opcode == ByteOps.JSR) {
+            // JSR has no Rop instruction
+            hasJsr = true;
+            return;
+        } else if (opcode == ByteOps.RET) {
+            try {
+                returnAddress = (ReturnAddress)arg(0);
+            } catch (ClassCastException ex) {
+                throw new RuntimeException(
+                        "Argument to RET was not a ReturnAddress", ex);
+            }
+            // RET has no Rop instruction.
+            return;
+        }
+
+        ropOpcode = jopToRopOpcode(opcode, cst);
+
+        rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+
+        Insn moveResult = null;
+        if (dest != null && rop.isCallLike()) {
+            // We're going to want to have a move-result in the next basic block
+            extraBlockCount++;
+
+            moveResult = new PlainInsn(
+                    Rops.opMoveResult(((CstMethodRef) cst).getPrototype()
+                    .getReturnType()), pos, dest, RegisterSpecList.EMPTY);
+
+            dest = null;
+        } else if (dest != null && rop.canThrow()) {
+            // We're going to want to have a move-result-pseudo
+            // in the next basic block
+            extraBlockCount++;
+
+            moveResult = new PlainInsn(
+                    Rops.opMoveResultPseudo(dest.getTypeBearer()),
+                    pos, dest, RegisterSpecList.EMPTY);
+
+            dest = null;
+        }
+        if (ropOpcode == RegOps.NEW_ARRAY) {
+            /*
+             * In the original bytecode, this was either a primitive
+             * array constructor "newarray" or an object array
+             * constructor "anewarray". In the former case, there is
+             * no explicit constant, and in the latter, the constant
+             * is for the element type and not the array type. The rop
+             * instruction form for both of these is supposed to be
+             * the resulting array type, so we initialize / alter
+             * "cst" here, accordingly. Conveniently enough, the rop
+             * opcode already gets constructed with the proper array
+             * type.
+             */
+            cst = CstType.intern(rop.getResult());
+        } else if ((cst == null) && (sourceCount == 2)) {
+            TypeBearer lastType = sources.get(1).getTypeBearer();
+
+            if (lastType.isConstant()
+                    && advice.hasConstantOperation(rop,
+                    sources.get(0), sources.get(1))) {
+                /*
+                 * The target architecture has an instruction that can
+                 * build in the constant found in the second argument,
+                 * so pull it out of the sources and just use it as a
+                 * constant here.
+                 */
+                cst = (Constant) lastType;
+                sources = sources.withoutLast();
+                rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+            }
+        }
+
+        SwitchList cases = getAuxCases();
+        ArrayList<Constant> initValues = getInitValues();
+        boolean canThrow = rop.canThrow();
+
+        blockCanThrow |= canThrow;
+
+        if (cases != null) {
+            if (cases.size() == 0) {
+                // It's a default-only switch statement. It can happen!
+                insn = new PlainInsn(Rops.GOTO, pos, null,
+                                     RegisterSpecList.EMPTY);
+                primarySuccessorIndex = 0;
+            } else {
+                IntList values = cases.getValues();
+                insn = new SwitchInsn(rop, pos, dest, sources, values);
+                primarySuccessorIndex = values.size();
+            }
+        } else if (ropOpcode == RegOps.RETURN) {
+            /*
+             * Returns get turned into the combination of a move (if
+             * non-void and if the return doesn't already mention
+             * register 0) and a goto (to the return block).
+             */
+            if (sources.size() != 0) {
+                RegisterSpec source = sources.get(0);
+                TypeBearer type = source.getTypeBearer();
+                if (source.getReg() != 0) {
+                    insns.add(new PlainInsn(Rops.opMove(type), pos,
+                                            RegisterSpec.make(0, type),
+                                            source));
+                }
+            }
+            insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
+            primarySuccessorIndex = 0;
+            updateReturnOp(rop, pos);
+            returns = true;
+        } else if (cst != null) {
+            if (canThrow) {
+                insn =
+                    new ThrowingCstInsn(rop, pos, sources, catches, cst);
+                catchesUsed = true;
+                primarySuccessorIndex = catches.size();
+            } else {
+                insn = new PlainCstInsn(rop, pos, dest, sources, cst);
+            }
+        } else if (canThrow) {
+            insn = new ThrowingInsn(rop, pos, sources, catches);
+            catchesUsed = true;
+            if (opcode == ByteOps.ATHROW) {
+                /*
+                 * The op athrow is the only one where it's possible
+                 * to have non-empty successors and yet not have a
+                 * primary successor.
+                 */
+                primarySuccessorIndex = -1;
+            } else {
+                primarySuccessorIndex = catches.size();
+            }
+        } else {
+            insn = new PlainInsn(rop, pos, dest, sources);
+        }
+
+        insns.add(insn);
+
+        if (moveResult != null) {
+            insns.add(moveResult);
+        }
+
+        /*
+         * If initValues is non-null, it means that the parser has seen a group
+         * of compatible constant initialization bytecodes that are applied to
+         * the current newarray. The action we take here is to convert these
+         * initialization bytecodes into a single fill-array-data ROP which lays
+         * out all the constant values in a table.
+         */ 
+        if (initValues != null) {
+            extraBlockCount++;
+            insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
+                    RegisterSpecList.make(moveResult.getResult()), initValues,
+                    cst);
+            insns.add(insn);
+        }
+    }
+
+    /**
+     * Helper for {@link #run}, which gets the list of sources for the.
+     * instruction.
+     * 
+     * @param opcode the opcode being translated
+     * @param stackPointer &gt;= 0; the stack pointer after the instruction's
+     * arguments have been popped
+     * @return non-null; the sources
+     */
+    private RegisterSpecList getSources(int opcode, int stackPointer) {
+        int count = argCount();
+
+        if (count == 0) {
+            // We get an easy out if there aren't any sources.
+            return RegisterSpecList.EMPTY;
+        } 
+
+        int localIndex = getLocalIndex();
+        RegisterSpecList sources;
+
+        if (localIndex >= 0) {
+            // The instruction is operating on a local variable.
+            sources = new RegisterSpecList(1);
+            sources.set(0, RegisterSpec.make(localIndex, arg(0)));
+        } else {
+            sources = new RegisterSpecList(count);
+            int regAt = stackPointer;
+            for (int i = 0; i < count; i++) {
+                RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
+                sources.set(i, spec);
+                regAt += spec.getCategory();
+            }
+
+            switch (opcode) {
+                case ByteOps.IASTORE: {
+                    /*
+                     * The Java argument order for array stores is
+                     * (array, index, value), but the rop argument
+                     * order is (value, array, index). The following
+                     * code gets the right arguments in the right
+                     * places.
+                     */
+                    if (count != 3) {
+                        throw new RuntimeException("shouldn't happen");
+                    }
+                    RegisterSpec array = sources.get(0);
+                    RegisterSpec index = sources.get(1);
+                    RegisterSpec value = sources.get(2);
+                    sources.set(0, value);
+                    sources.set(1, array);
+                    sources.set(2, index);
+                    break;
+                }
+                case ByteOps.PUTFIELD: {
+                    /*
+                     * Similar to above: The Java argument order for
+                     * putfield is (object, value), but the rop
+                     * argument order is (value, object).
+                     */
+                    if (count != 2) {
+                        throw new RuntimeException("shouldn't happen");
+                    }
+                    RegisterSpec obj = sources.get(0);
+                    RegisterSpec value = sources.get(1);
+                    sources.set(0, value);
+                    sources.set(1, obj);
+                    break;
+                }
+            }
+        }
+
+        sources.setImmutable();
+        return sources;
+    }
+
+    /**
+     * Sets or updates the information about the return block.
+     * 
+     * @param op non-null; the opcode to use
+     * @param pos non-null; the position to use
+     */
+    private void updateReturnOp(Rop op, SourcePosition pos) {
+        if (op == null) {
+            throw new NullPointerException("op == null");
+        }
+
+        if (pos == null) {
+            throw new NullPointerException("pos == null");
+        }
+
+        if (returnOp == null) {
+            returnOp = op;
+            returnPosition = pos;
+        } else {
+            if (returnOp != op) {
+                throw new SimException("return op mismatch: " + op + ", " +
+                                       returnOp);
+            }
+
+            if (pos.getLine() > returnPosition.getLine()) {
+                // Pick the largest line number to be the "canonical" return.
+                returnPosition = pos;
+            }
+        }
+    }
+
+    /**
+     * Gets the register opcode for the given Java opcode.
+     * 
+     * @param jop &gt;= 0; the Java opcode
+     * @param cst null-ok; the constant argument, if any
+     * @return &gt;= 0; the corresponding register opcode
+     */
+    private int jopToRopOpcode(int jop, Constant cst) {
+        switch (jop) {
+            case ByteOps.POP:
+            case ByteOps.POP2:
+            case ByteOps.DUP:
+            case ByteOps.DUP_X1:
+            case ByteOps.DUP_X2:
+            case ByteOps.DUP2:
+            case ByteOps.DUP2_X1:
+            case ByteOps.DUP2_X2:
+            case ByteOps.SWAP:
+            case ByteOps.JSR:
+            case ByteOps.RET:
+            case ByteOps.MULTIANEWARRAY: {
+                // These need to be taken care of specially.
+                break;
+            }
+            case ByteOps.NOP: {
+                return RegOps.NOP;
+            }
+            case ByteOps.LDC:
+            case ByteOps.LDC2_W: {
+                return RegOps.CONST;
+            }
+            case ByteOps.ILOAD: 
+            case ByteOps.ISTORE: {
+                return RegOps.MOVE;
+            }
+            case ByteOps.IALOAD: {
+                return RegOps.AGET;
+            }
+            case ByteOps.IASTORE: {
+                return RegOps.APUT;
+            }
+            case ByteOps.IADD:
+            case ByteOps.IINC: {
+                return RegOps.ADD;
+            }
+            case ByteOps.ISUB: {
+                return RegOps.SUB;
+            }
+            case ByteOps.IMUL: {
+                return RegOps.MUL;
+            }
+            case ByteOps.IDIV: {
+                return RegOps.DIV;
+            }
+            case ByteOps.IREM: {
+                return RegOps.REM;
+            }
+            case ByteOps.INEG: {
+                return RegOps.NEG;
+            }
+            case ByteOps.ISHL: {
+                return RegOps.SHL;
+            }
+            case ByteOps.ISHR: {
+                return RegOps.SHR;
+            }
+            case ByteOps.IUSHR: {
+                return RegOps.USHR;
+            }
+            case ByteOps.IAND: {
+                return RegOps.AND;
+            }
+            case ByteOps.IOR: {
+                return RegOps.OR;
+            }
+            case ByteOps.IXOR: {
+                return RegOps.XOR;
+            }
+            case ByteOps.I2L:
+            case ByteOps.I2F:
+            case ByteOps.I2D:
+            case ByteOps.L2I:
+            case ByteOps.L2F:
+            case ByteOps.L2D:
+            case ByteOps.F2I:
+            case ByteOps.F2L:
+            case ByteOps.F2D:
+            case ByteOps.D2I:
+            case ByteOps.D2L:
+            case ByteOps.D2F: {
+                return RegOps.CONV;
+            }
+            case ByteOps.I2B: {
+                return RegOps.TO_BYTE;
+            }
+            case ByteOps.I2C: {
+                return RegOps.TO_CHAR;
+            }
+            case ByteOps.I2S: {
+                return RegOps.TO_SHORT;
+            }
+            case ByteOps.LCMP:
+            case ByteOps.FCMPL:
+            case ByteOps.DCMPL: {
+                return RegOps.CMPL;
+            }
+            case ByteOps.FCMPG:
+            case ByteOps.DCMPG: {
+                return RegOps.CMPG;
+            }
+            case ByteOps.IFEQ:
+            case ByteOps.IF_ICMPEQ:
+            case ByteOps.IF_ACMPEQ:
+            case ByteOps.IFNULL: {
+                return RegOps.IF_EQ;
+            }
+            case ByteOps.IFNE:
+            case ByteOps.IF_ICMPNE:
+            case ByteOps.IF_ACMPNE:
+            case ByteOps.IFNONNULL: {
+                return RegOps.IF_NE;
+            }
+            case ByteOps.IFLT:
+            case ByteOps.IF_ICMPLT: {
+                return RegOps.IF_LT;
+            }
+            case ByteOps.IFGE:
+            case ByteOps.IF_ICMPGE: {
+                return RegOps.IF_GE;
+            }
+            case ByteOps.IFGT:
+            case ByteOps.IF_ICMPGT: {
+                return RegOps.IF_GT;
+            }
+            case ByteOps.IFLE:
+            case ByteOps.IF_ICMPLE: {
+                return RegOps.IF_LE;
+            }
+            case ByteOps.GOTO: {
+                return RegOps.GOTO;
+            }
+            case ByteOps.LOOKUPSWITCH: {
+                return RegOps.SWITCH;
+            }
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN: {
+                return RegOps.RETURN;
+            }
+            case ByteOps.GETSTATIC: {
+                return RegOps.GET_STATIC;
+            }
+            case ByteOps.PUTSTATIC: {
+                return RegOps.PUT_STATIC;
+            }
+            case ByteOps.GETFIELD: {
+                return RegOps.GET_FIELD;
+            }
+            case ByteOps.PUTFIELD: {
+                return RegOps.PUT_FIELD;
+            }
+            case ByteOps.INVOKEVIRTUAL: {
+                return RegOps.INVOKE_VIRTUAL;
+            }
+            case ByteOps.INVOKESPECIAL: {
+                /*
+                 * Determine whether the opcode should be
+                 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
+                 * on "invokespecial" as well as section 4.8.2 (7th
+                 * bullet point) for the gory details.
+                 */
+                CstMethodRef ref = (CstMethodRef) cst;
+                if (ref.isInstanceInit() ||
+                    (ref.getDefiningClass() == method.getDefiningClass()) ||
+                    !method.getAccSuper()) {
+                    return RegOps.INVOKE_DIRECT;
+                }
+                return RegOps.INVOKE_SUPER;
+            }
+            case ByteOps.INVOKESTATIC: {
+                return RegOps.INVOKE_STATIC;
+            }
+            case ByteOps.INVOKEINTERFACE: {
+                return RegOps.INVOKE_INTERFACE;
+            }
+            case ByteOps.NEW: {
+                return RegOps.NEW_INSTANCE;
+            }
+            case ByteOps.NEWARRAY:
+            case ByteOps.ANEWARRAY: {
+                return RegOps.NEW_ARRAY;
+            }
+            case ByteOps.ARRAYLENGTH: {
+                return RegOps.ARRAY_LENGTH;
+            }
+            case ByteOps.ATHROW: {
+                return RegOps.THROW;
+            }
+            case ByteOps.CHECKCAST: {
+                return RegOps.CHECK_CAST;
+            }
+            case ByteOps.INSTANCEOF: {
+                return RegOps.INSTANCE_OF;
+            }
+            case ByteOps.MONITORENTER: {
+                return RegOps.MONITOR_ENTER;
+            }
+            case ByteOps.MONITOREXIT: {
+                return RegOps.MONITOR_EXIT;
+            }
+        }
+
+        throw new RuntimeException("shouldn't happen");
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/SimException.java b/dx/src/com/android/dx/cf/code/SimException.java
new file mode 100644
index 0000000..220f281
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SimException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from simulation.
+ */
+public class SimException
+        extends ExceptionWithContext {
+    public SimException(String message) {
+        super(message);
+    }
+
+    public SimException(Throwable cause) {
+        super(cause);
+    }
+
+    public SimException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
new file mode 100644
index 0000000..3c90ee5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Class which knows how to simulate the effects of executing bytecode.
+ * 
+ * <p><b>Note:</b> This class is not thread-safe. If multiple threads
+ * need to use a single instance, they must synchronize access explicitly
+ * between themselves.</p>
+ */
+public class Simulator {
+    /** non-null; canned error message for local variable table mismatches */
+    private static final String LOCAL_MISMATCH_ERROR = 
+        "This is symptomatic of .class transformation tools that ignore " +
+        "local variable information.";
+    
+    /** non-null; machine to use when simulating */
+    private final Machine machine;
+
+    /** non-null; array of bytecode */
+    private final BytecodeArray code;
+
+    /** non-null; local variable information */
+    private final LocalVariableList localVariables;
+
+    /** non-null; visitor instance to use */
+    private final SimVisitor visitor;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param machine non-null; machine to use when simulating
+     * @param method non-null; method data to use
+     */
+    public Simulator(Machine machine, ConcreteMethod method) {
+        if (machine == null) {
+            throw new NullPointerException("machine == null");
+        }
+
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.machine = machine;
+        this.code = method.getCode();
+        this.localVariables = method.getLocalVariables();
+        this.visitor = new SimVisitor();
+    }
+
+    /**
+     * Simulates the effect of executing the given basic block. This modifies
+     * the passed-in frame to represent the end result.
+     * 
+     * @param bb non-null; the basic block
+     * @param frame non-null; frame to operate on
+     */
+    public void simulate(ByteBlock bb, Frame frame) {
+        int end = bb.getEnd();
+
+        visitor.setFrame(frame);
+
+        try {
+            for (int off = bb.getStart(); off < end; /*off*/) {
+                int length = code.parseInstruction(off, visitor);
+                visitor.setPreviousOffset(off);
+                off += length;
+            }
+        } catch (SimException ex) {
+            frame.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Simulates the effect of the instruction at the given offset, by
+     * making appropriate calls on the given frame.
+     * 
+     * @param offset &gt;= 0; offset of the instruction to simulate
+     * @param frame non-null; frame to operate on
+     * @return the length of the instruction, in bytes
+     */
+    public int simulate(int offset, Frame frame) {
+        visitor.setFrame(frame);
+        return code.parseInstruction(offset, visitor);
+    }
+
+    /**
+     * Constructs an "illegal top-of-stack" exception, for the stack
+     * manipulation opcodes.
+     */
+    private static SimException illegalTos() {
+        return new SimException("stack mismatch: illegal " +
+                "top-of-stack for opcode");
+    }
+
+    /**
+     * Bytecode visitor used during simulation.
+     */
+    private class SimVisitor implements BytecodeArray.Visitor {
+        /**
+         * non-null; machine instance to use (just to avoid excessive
+         * cross-object field access) 
+         */
+        private final Machine machine;
+
+        /**
+         * null-ok; frame to use; set with each call to 
+         * {@link Simulator#simulate}
+         */
+        private Frame frame;
+
+        /** offset of the previous bytecode */
+        private int previousOffset;
+
+        /**
+         * Constructs an instance.
+         */
+        public SimVisitor() {
+            this.machine = Simulator.this.machine;
+            this.frame = null;
+        }
+
+        /**
+         * Sets the frame to act on.
+         * 
+         * @param frame non-null; the frame
+         */
+        public void setFrame(Frame frame) {
+            if (frame == null) {
+                throw new NullPointerException("frame == null");
+            }
+
+            this.frame = frame;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            throw new SimException("invalid opcode " + Hex.u1(opcode));
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            switch (opcode) {
+                case ByteOps.NOP: {
+                    machine.clearArgs();
+                    break;
+                }
+                case ByteOps.INEG: {
+                    machine.popArgs(frame, type);
+                    break;
+                }
+                case ByteOps.I2L:
+                case ByteOps.I2F:
+                case ByteOps.I2D:
+                case ByteOps.I2B:
+                case ByteOps.I2C:
+                case ByteOps.I2S: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.L2I:
+                case ByteOps.L2F:
+                case ByteOps.L2D: {
+                    machine.popArgs(frame, Type.LONG);
+                    break;
+                }
+                case ByteOps.F2I:
+                case ByteOps.F2L:
+                case ByteOps.F2D: {
+                    machine.popArgs(frame, Type.FLOAT);
+                    break;
+                }
+                case ByteOps.D2I:
+                case ByteOps.D2L:
+                case ByteOps.D2F: {
+                    machine.popArgs(frame, Type.DOUBLE);
+                    break;
+                }
+                case ByteOps.RETURN: {
+                    machine.clearArgs();
+                    checkReturnType(Type.VOID);
+                    break;
+                }
+                case ByteOps.IRETURN: {
+                    Type checkType = type;
+                    if (type == Type.OBJECT) {
+                        /*
+                         * For an object return, use the best-known
+                         * type of the popped value.
+                         */
+                        checkType = frame.getStack().peekType(0);
+                    }
+                    machine.popArgs(frame, type);
+                    checkReturnType(checkType);
+                    break;
+                }
+                case ByteOps.POP: {
+                    Type peekType = frame.getStack().peekType(0);
+                    if (peekType.isCategory2()) {
+                        throw illegalTos();
+                    }
+                    machine.popArgs(frame, 1);
+                    break;
+                }
+                case ByteOps.ARRAYLENGTH: {
+                    Type arrayType = frame.getStack().peekType(0);
+                    if (!arrayType.isArrayOrKnownNull()) {
+                        throw new SimException("type mismatch: expected " +
+                                "array type but encountered " +
+                                arrayType.toHuman());
+                    }
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.ATHROW:
+                case ByteOps.MONITORENTER:
+                case ByteOps.MONITOREXIT: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.IALOAD: {
+                    /*
+                     * Change the type (which is to be pushed) to
+                     * reflect the actual component type of the array
+                     * being popped.
+                     */
+                    Type requireType = type.getArrayType();
+                    type = frame.getStack().peekType(1);
+                    if (type == Type.KNOWN_NULL) {
+                        /*
+                         * The type is a known-null: Just treat the
+                         * popped type as whatever is expected. In
+                         * reality, unless this frame is revisited
+                         * (due to a branch merge), execution will
+                         * result in the throwing of a
+                         * NullPointerException, but claiming the
+                         * expected type at here should be good enough
+                         * for the purposes at this level.
+                         */
+                        type = requireType;
+                    }
+                    type = type.getComponentType();
+                    machine.popArgs(frame, requireType, Type.INT);
+                    break;
+                }
+                case ByteOps.IADD:
+                case ByteOps.ISUB:
+                case ByteOps.IMUL:
+                case ByteOps.IDIV:
+                case ByteOps.IREM:
+                case ByteOps.IAND:
+                case ByteOps.IOR:
+                case ByteOps.IXOR: {
+                    machine.popArgs(frame, type, type);
+                    break;
+                }
+                case ByteOps.ISHL:
+                case ByteOps.ISHR:
+                case ByteOps.IUSHR: {
+                    machine.popArgs(frame, type, Type.INT);
+                    break;
+                }                    
+                case ByteOps.LCMP: {
+                    machine.popArgs(frame, Type.LONG, Type.LONG);
+                    break;
+                }
+                case ByteOps.FCMPL:
+                case ByteOps.FCMPG: {
+                    machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
+                    break;
+                }
+                case ByteOps.DCMPL:
+                case ByteOps.DCMPG: {
+                    machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
+                    break;
+                }
+                case ByteOps.IASTORE: {
+                    Type arrayType = type.getArrayType();
+                    machine.popArgs(frame, arrayType, Type.INT, type);
+                    break;
+                }
+                case ByteOps.POP2:
+                case ByteOps.DUP2: {
+                    ExecutionStack stack = frame.getStack();
+                    int pattern;
+
+                    if (stack.peekType(0).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        machine.popArgs(frame, 1);
+                        pattern = 0x11;
+                    } else if (stack.peekType(1).isCategory1()) {
+                        // "form 1"
+                        machine.popArgs(frame, 2);
+                        pattern = 0x2121;
+                    } else {
+                        throw illegalTos();
+                    }
+
+                    if (opcode == ByteOps.DUP2) {
+                        machine.auxIntArg(pattern);
+                    }
+                    break;
+                }
+                case ByteOps.DUP: {
+                    Type peekType = frame.getStack().peekType(0);
+
+                    if (peekType.isCategory2()) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 1);
+                    machine.auxIntArg(0x11);
+                    break;
+                }
+                case ByteOps.DUP_X1: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (! (stack.peekType(0).isCategory1() &&
+                           stack.peekType(1).isCategory1())) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 2);
+                    machine.auxIntArg(0x212);
+                    break;
+                }
+                case ByteOps.DUP_X2: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        throw illegalTos();
+                    }
+                    
+                    if (stack.peekType(1).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        machine.popArgs(frame, 2);
+                        machine.auxIntArg(0x212);
+                    } else if (stack.peekType(2).isCategory1()) {
+                        // "form 1"
+                        machine.popArgs(frame, 3);
+                        machine.auxIntArg(0x3213);
+                    } else {
+                        throw illegalTos();
+                    }
+                    break;
+                }
+                case ByteOps.DUP2_X1: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        if (stack.peekType(2).isCategory2()) {
+                            throw illegalTos();
+                        }
+                        machine.popArgs(frame, 2);
+                        machine.auxIntArg(0x212);
+                    } else {
+                        // "form 1"
+                        if (stack.peekType(1).isCategory2() ||
+                            stack.peekType(2).isCategory2()) {
+                            throw illegalTos();
+                        }
+                        machine.popArgs(frame, 3);
+                        machine.auxIntArg(0x32132);
+                    }
+                    break;
+                }
+                case ByteOps.DUP2_X2: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        if (stack.peekType(2).isCategory2()) {
+                            // "form 4" in vmspec-2
+                            machine.popArgs(frame, 2);
+                            machine.auxIntArg(0x212);
+                        } else if (stack.peekType(3).isCategory1()) {
+                            // "form 2"
+                            machine.popArgs(frame, 3);
+                            machine.auxIntArg(0x3213);
+                        } else {
+                            throw illegalTos();
+                        }
+                    } else if (stack.peekType(1).isCategory1()) {
+                        if (stack.peekType(2).isCategory2()) {
+                            // "form 3"
+                            machine.popArgs(frame, 3);
+                            machine.auxIntArg(0x32132);
+                        } else if (stack.peekType(3).isCategory1()) {
+                            // "form 1"
+                            machine.popArgs(frame, 4);
+                            machine.auxIntArg(0x432143);
+                        } else {
+                            throw illegalTos();
+                        }
+                    } else {
+                        throw illegalTos();
+                    }
+                    break;
+                }
+                case ByteOps.SWAP: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (! (stack.peekType(0).isCategory1() &&
+                           stack.peekType(1).isCategory1())) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 2);
+                    machine.auxIntArg(0x12);
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.auxType(type);
+            machine.run(frame, offset, opcode);
+        }
+
+        /**
+         * Checks whether the prototype is compatible with returning the
+         * given type, and throws if not.
+         * 
+         * @param encountered non-null; the encountered return type
+         */
+        private void checkReturnType(Type encountered) {
+            Type returnType = machine.getPrototype().getReturnType();
+
+            /*
+             * Check to see if the prototype's return type is
+             * possibly assignable from the type we encountered. This
+             * takes care of all the salient cases (types are the same,
+             * they're compatible primitive types, etc.).
+             */
+            if (! Merger.isPossiblyAssignableFrom(returnType, encountered)) {
+                throw new SimException("return type mismatch: prototype " +
+                        "indicates " + returnType.toHuman() +
+                        ", but encountered type " + encountered.toHuman());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            /*
+             * Note that the "type" parameter is always the simplest
+             * type based on the original opcode, e.g., "int" for
+             * "iload" (per se) and "Object" for "aload". So, when
+             * possible, we replace the type with the one indicated in
+             * the local variable table, though we still need to check
+             * to make sure it's valid for the opcode.
+             * 
+             * The reason we use (offset + length) for the localOffset
+             * for a store is because it is only after the store that
+             * the local type becomes valid. On the other hand, the
+             * type associated with a load is valid at the start of
+             * the instruction.
+             */
+            int localOffset =
+                (opcode == ByteOps.ISTORE) ? (offset + length) : offset;
+            LocalVariableList.Item local =
+                localVariables.pcAndIndexToLocal(localOffset, idx);
+            Type localType;
+
+            if (local != null) {
+                localType = local.getType();
+                if (localType.getBasicFrameType() !=
+                        type.getBasicFrameType()) {
+                    BaseMachine.throwLocalMismatch(type, localType);
+                    return;
+                }
+            } else {
+                localType = type;
+            }
+
+            switch (opcode) {
+                case ByteOps.ILOAD:
+                case ByteOps.RET: {
+                    machine.localArg(frame, idx);
+                    machine.auxType(type);
+                    break;
+                }
+                case ByteOps.ISTORE: {
+                    LocalItem item
+                            = (local == null) ? null : local.getLocalItem();
+                    machine.popArgs(frame, type);
+                    machine.auxType(type);
+                    machine.localTarget(idx, localType, item);
+                    break;
+                }
+                case ByteOps.IINC: {
+                    LocalItem item
+                            = (local == null) ? null : local.getLocalItem();
+                    machine.localArg(frame, idx);
+                    machine.localTarget(idx, localType, item);
+                    machine.auxType(type);
+                    machine.auxIntArg(value);
+                    machine.auxCstArg(CstInteger.make(value));
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            switch (opcode) {
+                case ByteOps.ANEWARRAY: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.PUTSTATIC: {
+                    Type fieldType = ((CstFieldRef) cst).getType();
+                    machine.popArgs(frame, fieldType);
+                    break;
+                }
+                case ByteOps.GETFIELD:
+                case ByteOps.CHECKCAST:
+                case ByteOps.INSTANCEOF: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.PUTFIELD: {
+                    Type fieldType = ((CstFieldRef) cst).getType();
+                    machine.popArgs(frame, Type.OBJECT, fieldType);
+                    break;
+                }
+                case ByteOps.INVOKEINTERFACE: {
+                    /*
+                     * Convert the interface method ref into a normal
+                     * method ref.
+                     */
+                    cst = ((CstInterfaceMethodRef) cst).toMethodRef();
+                    // and fall through...
+                }
+                case ByteOps.INVOKEVIRTUAL:
+                case ByteOps.INVOKESPECIAL: {
+                    /*
+                     * Get the instance prototype, and use it to direct
+                     * the machine.
+                     */
+                    Prototype prototype = 
+                        ((CstMethodRef) cst).getPrototype(false);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                case ByteOps.INVOKESTATIC: {
+                    /*
+                     * Get the static prototype, and use it to direct
+                     * the machine.
+                     */
+                    Prototype prototype = 
+                        ((CstMethodRef) cst).getPrototype(true);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                case ByteOps.MULTIANEWARRAY: {
+                    /*
+                     * The "value" here is the count of dimensions to
+                     * create. Make a prototype of that many "int"
+                     * types, and tell the machine to pop them. This
+                     * isn't the most efficient way in the world to do
+                     * this, but then again, multianewarray is pretty
+                     * darn rare and so not worth much effort
+                     * optimizing for.
+                     */
+                    Prototype prototype =
+                        Prototype.internInts(Type.VOID, value);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                default: {
+                    machine.clearArgs();
+                    break;
+                }
+            }
+
+            machine.auxIntArg(value);
+            machine.auxCstArg(cst);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            switch (opcode) {
+                case ByteOps.IFEQ:
+                case ByteOps.IFNE:
+                case ByteOps.IFLT:
+                case ByteOps.IFGE:
+                case ByteOps.IFGT:
+                case ByteOps.IFLE: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.IFNULL:
+                case ByteOps.IFNONNULL: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.IF_ICMPEQ:
+                case ByteOps.IF_ICMPNE:
+                case ByteOps.IF_ICMPLT:
+                case ByteOps.IF_ICMPGE:
+                case ByteOps.IF_ICMPGT:
+                case ByteOps.IF_ICMPLE: {
+                    machine.popArgs(frame, Type.INT, Type.INT);
+                    break;
+                }
+                case ByteOps.IF_ACMPEQ:
+                case ByteOps.IF_ACMPNE: {
+                    machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.GOTO:
+                case ByteOps.JSR:
+                case ByteOps.GOTO_W:
+                case ByteOps.JSR_W: {
+                    machine.clearArgs();
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.auxTargetArg(target);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            machine.popArgs(frame, Type.INT);
+            machine.auxIntArg(padding);
+            machine.auxSwitchArg(cases);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initValues) {
+            machine.popArgs(frame, Type.INT);
+            machine.auxInitValues(initValues);
+            machine.auxCstArg(type);
+            machine.run(frame, offset, ByteOps.NEWARRAY);
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            previousOffset = offset;
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            return previousOffset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/SwitchList.java b/dx/src/com/android/dx/cf/code/SwitchList.java
new file mode 100644
index 0000000..dc04137
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SwitchList.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.IntList;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * List of (value, target) mappings representing the choices of a
+ * <code>tableswitch</code> or <code>lookupswitch</code> instruction. It
+ * also holds the default target for the switch.
+ */
+public final class SwitchList extends MutabilityControl {
+    /** non-null; list of test values */
+    private final IntList values;
+
+    /**
+     * non-null; list of targets corresponding to the test values; there
+     * is always one extra element in the target list, to hold the
+     * default target 
+     */
+    private final IntList targets;
+
+    /** ultimate size of the list */
+    private int size;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param size &gt;= 0; the number of elements to be in the table
+     */
+    public SwitchList(int size) {
+        super(true);
+        this.values = new IntList(size);
+        this.targets = new IntList(size + 1);
+        this.size = size;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setImmutable() {
+        values.setImmutable();
+        targets.setImmutable();
+        super.setImmutable();
+    }
+
+    /**
+     * Gets the size of the list.
+     * 
+     * @return &gt;= 0; the list size
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Gets the indicated test value.
+     * 
+     * @param n &gt;= 0;, &lt; size(); which index
+     * @return the test value 
+     */
+    public int getValue(int n) {
+        return values.get(n);
+    }
+
+    /**
+     * Gets the indicated target. Asking for the target at <code>size()</code>
+     * returns the default target.
+     * 
+     * @param n &gt;= 0, &lt;= size(); which index
+     * @return &gt;= 0; the target
+     */
+    public int getTarget(int n) {
+        return targets.get(n);
+    }
+
+    /**
+     * Gets the default target. This is just a shorthand for
+     * <code>getTarget(size())</code>.
+     * 
+     * @return &gt;= 0; the default target
+     */
+    public int getDefaultTarget() {
+        return targets.get(size);
+    }
+
+    /**
+     * Gets the list of all targets. This includes one extra element at the
+     * end of the list, which holds the default target.
+     * 
+     * @return non-null; the target list
+     */
+    public IntList getTargets() {
+        return targets;
+    }
+
+    /**
+     * Gets the list of all case values.
+     * 
+     * @return non-null; the case value list
+     */
+    public IntList getValues() {
+        return values;
+    }
+
+    /**
+     * Sets the default target. It is only valid to call this method
+     * when all the non-default elements have been set.
+     * 
+     * @param target &gt;= 0; the absolute (not relative) default target
+     * address
+     */
+    public void setDefaultTarget(int target) {
+        throwIfImmutable();
+
+        if (target < 0) {
+            throw new IllegalArgumentException("target < 0");
+        }
+
+        if (targets.size() != size) {
+            throw new RuntimeException("non-default elements not all set");
+        }
+
+        targets.add(target);
+    }
+
+    /**
+     * Adds the given item.
+     * 
+     * @param value the test value
+     * @param target &gt;= 0; the absolute (not relative) target address
+     */
+    public void add(int value, int target) {
+        throwIfImmutable();
+
+        if (target < 0) {
+            throw new IllegalArgumentException("target < 0");
+        }
+
+        values.add(value);
+        targets.add(target);
+    }
+
+    /**
+     * Shrinks this instance if possible, removing test elements that
+     * refer to the default target. This is only valid after the instance
+     * is fully populated, including the default target (naturally).
+     */
+    public void removeSuperfluousDefaults() {
+        throwIfImmutable();
+
+        int sz = size;
+
+        if (sz != (targets.size() - 1)) {
+            throw new IllegalArgumentException("incomplete instance");
+        }
+
+        int defaultTarget = targets.get(sz);
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            int target = targets.get(i);
+            if (target != defaultTarget) {
+                if (i != at) {
+                    targets.set(at, target);
+                    values.set(at, values.get(i));
+                }
+                at++;
+            }
+        }
+
+        if (at != sz) {
+            values.shrink(at);
+            targets.set(at, defaultTarget);
+            targets.shrink(at + 1);
+            size = at;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ValueAwareMachine.java b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
new file mode 100644
index 0000000..4062c3b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * {@link Machine} which keeps track of known values but does not do
+ * smart/realistic reference type calculations.
+ */
+public class ValueAwareMachine extends BaseMachine {
+    /**
+     * Constructs an instance.
+     * 
+     * @param prototype non-null; the prototype for the associated method
+     */
+    public ValueAwareMachine(Prototype prototype) {
+        super(prototype);
+    }
+
+    /** {@inheritDoc} */
+    public void run(Frame frame, int offset, int opcode) {
+        switch (opcode) {
+            case ByteOps.NOP:
+            case ByteOps.IASTORE:
+            case ByteOps.POP:
+            case ByteOps.POP2:
+            case ByteOps.IFEQ:
+            case ByteOps.IFNE:
+            case ByteOps.IFLT:
+            case ByteOps.IFGE:
+            case ByteOps.IFGT:
+            case ByteOps.IFLE:
+            case ByteOps.IF_ICMPEQ:
+            case ByteOps.IF_ICMPNE:
+            case ByteOps.IF_ICMPLT:
+            case ByteOps.IF_ICMPGE:
+            case ByteOps.IF_ICMPGT:
+            case ByteOps.IF_ICMPLE:
+            case ByteOps.IF_ACMPEQ:
+            case ByteOps.IF_ACMPNE:
+            case ByteOps.GOTO:
+            case ByteOps.RET:
+            case ByteOps.LOOKUPSWITCH:
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN:
+            case ByteOps.PUTSTATIC:
+            case ByteOps.PUTFIELD:
+            case ByteOps.ATHROW:
+            case ByteOps.MONITORENTER:
+            case ByteOps.MONITOREXIT:
+            case ByteOps.IFNULL:
+            case ByteOps.IFNONNULL: {
+                // Nothing to do for these ops in this class.
+                clearResult();
+                break;
+            }
+            case ByteOps.LDC:
+            case ByteOps.LDC2_W: {
+                setResult((TypeBearer) getAuxCst());
+                break;
+            }
+            case ByteOps.ILOAD:
+            case ByteOps.ISTORE: {
+                setResult(arg(0));
+                break;
+            }
+            case ByteOps.IALOAD:
+            case ByteOps.IADD:
+            case ByteOps.ISUB:
+            case ByteOps.IMUL:
+            case ByteOps.IDIV:
+            case ByteOps.IREM:
+            case ByteOps.INEG:
+            case ByteOps.ISHL:
+            case ByteOps.ISHR:
+            case ByteOps.IUSHR:
+            case ByteOps.IAND:
+            case ByteOps.IOR:
+            case ByteOps.IXOR: 
+            case ByteOps.IINC:
+            case ByteOps.I2L:
+            case ByteOps.I2F:
+            case ByteOps.I2D:
+            case ByteOps.L2I:
+            case ByteOps.L2F:
+            case ByteOps.L2D:
+            case ByteOps.F2I:
+            case ByteOps.F2L:
+            case ByteOps.F2D:
+            case ByteOps.D2I:
+            case ByteOps.D2L:
+            case ByteOps.D2F:
+            case ByteOps.I2B:
+            case ByteOps.I2C:
+            case ByteOps.I2S:
+            case ByteOps.LCMP:
+            case ByteOps.FCMPL:
+            case ByteOps.FCMPG:
+            case ByteOps.DCMPL:
+            case ByteOps.DCMPG:
+            case ByteOps.ARRAYLENGTH: {
+                setResult(getAuxType());
+                break;
+            }
+            case ByteOps.DUP:
+            case ByteOps.DUP_X1:
+            case ByteOps.DUP_X2:
+            case ByteOps.DUP2:
+            case ByteOps.DUP2_X1:
+            case ByteOps.DUP2_X2:
+            case ByteOps.SWAP: {
+                clearResult();
+                for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+                    int which = (pattern & 0x0f) - 1;
+                    addResult(arg(which));
+                }
+                break;
+            }
+
+            case ByteOps.JSR: {
+                setResult(new ReturnAddress(getAuxTarget()));
+                break;
+            }
+            case ByteOps.GETSTATIC:
+            case ByteOps.GETFIELD:
+            case ByteOps.INVOKEVIRTUAL:
+            case ByteOps.INVOKESTATIC:
+            case ByteOps.INVOKEINTERFACE: {
+                Type type = ((TypeBearer) getAuxCst()).getType();
+                if (type == Type.VOID) {
+                    clearResult();
+                } else {
+                    setResult(type);
+                }
+                break;
+            }
+            case ByteOps.INVOKESPECIAL: {
+                Type thisType = arg(0).getType();
+                if (thisType.isUninitialized()) {
+                    frame.makeInitialized(thisType);
+                }
+                Type type = ((TypeBearer) getAuxCst()).getType();
+                if (type == Type.VOID) {
+                    clearResult();                    
+                } else {
+                    setResult(type);
+                }
+                break;
+            }
+            case ByteOps.NEW: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type.asUninitialized(offset));
+                break;
+            }
+            case ByteOps.NEWARRAY:
+            case ByteOps.CHECKCAST:
+            case ByteOps.MULTIANEWARRAY: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type);
+                break;
+            }
+            case ByteOps.ANEWARRAY: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type.getArrayType());
+                break;
+            }
+            case ByteOps.INSTANCEOF: {
+                setResult(Type.INT);
+                break;
+            }
+            default: {
+                throw new RuntimeException("shouldn't happen: " +
+                                           Hex.u1(opcode));
+            }
+        }
+
+        storeResults(frame);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/package.html b/dx/src/com/android/dx/cf/code/package.html
new file mode 100644
index 0000000..abd4e9b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Implementation of classes having to do with Java simulation, such as
+is needed for verification or stack-to-register conversion.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
new file mode 100644
index 0000000..953981c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.cst;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import static com.android.dx.cf.cst.ConstantTags.*;
+
+/**
+ * Parser for a constant pool embedded in a class file.
+ */
+public final class ConstantPoolParser {
+    /** non-null; the bytes of the constant pool */
+    private final ByteArray bytes;
+
+    /** non-null; actual parsed constant pool contents */
+    private final StdConstantPool pool;
+
+    /** non-null; byte offsets to each cst */
+    private final int[] offsets;
+
+    /**
+     * -1 || &gt;= 10; the end offset of this constant pool in the
+     * <code>byte[]</code> which it came from or <code>-1</code> if not
+     * yet parsed 
+     */
+    private int endOffset;
+
+    /** null-ok; parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; the bytes of the file
+     */
+    public ConstantPoolParser(ByteArray bytes) {
+        int size = bytes.getUnsignedShort(8); // constant_pool_count
+
+        this.bytes = bytes;
+        this.pool = new StdConstantPool(size);
+        this.offsets = new int[size];
+        this.endOffset = -1;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     * 
+     * @param observer null-ok; the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the <code>byte[]</code>
+     * which it came from.
+     * 
+     * @return &gt;= 10; the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Gets the actual constant pool.
+     * 
+     * @return non-null; the constant pool
+     */
+    public StdConstantPool getPool() {
+        parseIfNecessary();
+        return pool;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        determineOffsets();
+
+        if (observer != null) {
+            observer.parsed(bytes, 8, 2,
+                            "constant_pool_count: " + Hex.u2(offsets.length));
+            observer.parsed(bytes, 10, 0, "\nconstant_pool:");
+            observer.changeIndent(1);
+        }
+
+        for (int i = 1; i < offsets.length; i++) {
+            int offset = offsets[i];
+            if ((offset != 0) && (pool.getOrNull(i) == null)) {
+                parse0(i);
+            }
+        }
+
+        if (observer != null) {
+            for (int i = 1; i < offsets.length; i++) {
+                Constant cst = pool.getOrNull(i);
+                if (cst == null) {
+                    continue;
+                }
+                int offset = offsets[i];
+                int nextOffset = endOffset;
+                for (int j = i + 1; j < offsets.length; j++) {
+                    int off = offsets[j];
+                    if (off != 0) {
+                        nextOffset = off;
+                        break;
+                    }
+                }
+                observer.parsed(bytes, offset, nextOffset - offset,
+                                Hex.u2(i) + ": " + cst.toString());
+            }
+
+            observer.changeIndent(-1);
+            observer.parsed(bytes, endOffset, 0, "end constant_pool");
+        }
+    }
+
+    /**
+     * Populates {@link #offsets} and also completely parse utf8 constants.
+     */
+    private void determineOffsets() {
+        int at = 10; // offset from the start of the file to the first cst
+        int lastCategory;
+
+        for (int i = 1; i < offsets.length; i += lastCategory) {
+            offsets[i] = at;
+            int tag = bytes.getUnsignedByte(at);
+            switch (tag) {
+                case CONSTANT_Integer:
+                case CONSTANT_Float:
+                case CONSTANT_Fieldref:
+                case CONSTANT_Methodref:
+                case CONSTANT_InterfaceMethodref:
+                case CONSTANT_NameAndType: {
+                    lastCategory = 1;
+                    at += 5;
+                    break;
+                }
+                case CONSTANT_Long:
+                case CONSTANT_Double: {
+                    lastCategory = 2;
+                    at += 9;
+                    break;
+                }
+                case CONSTANT_Class:
+                case CONSTANT_String: {
+                    lastCategory = 1;
+                    at += 3;
+                    break;
+                }
+                case CONSTANT_Utf8: {
+                    lastCategory = 1;
+                    at += bytes.getUnsignedShort(at + 1) + 3;
+                    break;
+                }
+                default: {
+                    ParseException ex =
+                        new ParseException("unknown tag byte: " + Hex.u1(tag));
+                    ex.addContext("...while preparsing cst " + Hex.u2(i) +
+                                  " at offset " + Hex.u4(at));
+                    throw ex;
+                }
+            }
+        }
+
+        endOffset = at;
+    }
+
+    /**
+     * Parses the constant for the given index if it hasn't already been
+     * parsed, also storing it in the constant pool. This will also
+     * have the side effect of parsing any entries the indicated one
+     * depends on.
+     * 
+     * @param idx which constant
+     * @return non-null; the parsed constant
+     */
+    private Constant parse0(int idx) {
+        Constant cst = pool.getOrNull(idx);
+        if (cst != null) {
+            return cst;
+        }
+
+        int at = offsets[idx];
+
+        try {
+            int tag = bytes.getUnsignedByte(at);
+            switch (tag) {
+                case CONSTANT_Utf8: {
+                    cst = parseUtf8(at);
+                    break;
+                }
+                case CONSTANT_Integer: {
+                    int value = bytes.getInt(at + 1);
+                    cst = CstInteger.make(value);
+                    break;
+                }
+                case CONSTANT_Float: {
+                    int bits = bytes.getInt(at + 1);
+                    cst = CstFloat.make(bits);
+                    break;
+                }
+                case CONSTANT_Long: {
+                    long value = bytes.getLong(at + 1);
+                    cst = CstLong.make(value);
+                    break;
+                }
+                case CONSTANT_Double: {
+                    long bits = bytes.getLong(at + 1);
+                    cst = CstDouble.make(bits);
+                    break;
+                }
+                case CONSTANT_Class: {
+                    int nameIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 name = (CstUtf8) parse0(nameIndex);
+                    cst = new CstType(Type.internClassName(name.getString()));
+                    break;
+                }
+                case CONSTANT_String: {
+                    int stringIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 string = (CstUtf8) parse0(stringIndex);
+                    cst = new CstString(string);
+                    break;
+                }
+                case CONSTANT_Fieldref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstFieldRef(type, nat);
+                    break;
+                }
+                case CONSTANT_Methodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_InterfaceMethodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstInterfaceMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_NameAndType: {
+                    int nameIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 name = (CstUtf8) parse0(nameIndex);
+                    int descriptorIndex = bytes.getUnsignedShort(at + 3);
+                    CstUtf8 descriptor = (CstUtf8) parse0(descriptorIndex);
+                    cst = new CstNat(name, descriptor);
+                    break;
+                }
+            }
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing cst " + Hex.u2(idx) +
+                          " at offset " + Hex.u4(at));
+            throw ex;
+        } catch (RuntimeException ex) {
+            ParseException pe = new ParseException(ex);
+            pe.addContext("...while parsing cst " + Hex.u2(idx) +
+                          " at offset " + Hex.u4(at));
+            throw pe;
+        }
+
+        pool.set(idx, cst);
+        return cst;
+    }
+
+    /**
+     * Parses a utf8 constant.
+     * 
+     * @param at offset to the start of the constant (where the tag byte is)
+     * @return non-null; the parsed value
+     */
+    private CstUtf8 parseUtf8(int at) {
+        int length = bytes.getUnsignedShort(at + 1);
+
+        at += 3; // Skip to the data.
+
+        ByteArray ubytes = bytes.slice(at, at + length);
+
+        try {
+            return new CstUtf8(ubytes);
+        } catch (IllegalArgumentException ex) {
+            // Translate the exception
+            throw new ParseException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/cst/ConstantTags.java b/dx/src/com/android/dx/cf/cst/ConstantTags.java
new file mode 100644
index 0000000..64bc8d8
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantTags.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.cst;
+
+/**
+ * Tags for constant pool constants.
+ */
+public interface ConstantTags {
+    /** tag for a <code>CONSTANT_Utf8_info</code> */
+    int CONSTANT_Utf8 = 1;
+
+    /** tag for a <code>CONSTANT_Integer_info</code> */
+    int CONSTANT_Integer = 3;
+
+    /** tag for a <code>CONSTANT_Float_info</code> */
+    int CONSTANT_Float = 4;
+
+    /** tag for a <code>CONSTANT_Long_info</code> */
+    int CONSTANT_Long = 5;
+
+    /** tag for a <code>CONSTANT_Double_info</code> */
+    int CONSTANT_Double = 6;
+
+    /** tag for a <code>CONSTANT_Class_info</code> */
+    int CONSTANT_Class = 7;
+
+    /** tag for a <code>CONSTANT_String_info</code> */
+    int CONSTANT_String = 8;
+
+    /** tag for a <code>CONSTANT_Fieldref_info</code> */
+    int CONSTANT_Fieldref = 9;
+
+    /** tag for a <code>CONSTANT_Methodref_info</code> */
+    int CONSTANT_Methodref = 10;
+
+    /** tag for a <code>CONSTANT_InterfaceMethodref_info</code> */
+    int CONSTANT_InterfaceMethodref = 11;
+
+    /** tag for a <code>CONSTANT_NameAndType_info</code> */
+    int CONSTANT_NameAndType = 12;
+}
diff --git a/dx/src/com/android/dx/cf/direct/AnnotationParser.java b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
new file mode 100644
index 0000000..5d80086
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Parser for annotations.
+ */
+public final class AnnotationParser {
+    /** non-null; class file being parsed */
+    private final DirectClassFile cf;
+
+    /** non-null; constant pool to use */
+    private final ConstantPool pool;
+
+    /** non-null; bytes of the attribute data */
+    private final ByteArray bytes;
+
+    /** null-ok; parse observer, if any */
+    private final ParseObserver observer;
+
+    /** non-null; input stream to parse from */
+    private final ByteArray.MyDataInputStream input;
+
+    /**
+     * non-null; cursor for use when informing the observer of what
+     * was parsed
+     */
+    private int parseCursor;
+    
+    /**
+     * Constructs an instance.
+     * 
+     * @param cf non-null; class file to parse from
+     * @param offset &gt;= 0; offset into the class file data to parse at
+     * @param length &gt;= 0; number of bytes left in the attribute data
+     * @param observer null-ok; parse observer to notify, if any
+     */
+    public AnnotationParser(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        this.cf = cf;
+        this.pool = cf.getConstantPool();
+        this.observer = observer;
+        this.bytes = cf.getBytes().slice(offset, offset + length);
+        this.input = bytes.makeDataInputStream();
+        this.parseCursor = 0;
+    }
+    
+    /**
+     * Parses an annotation value (<code>element_value</code>) attribute.
+     * 
+     * @return non-null; the parsed constant value
+     */
+    public Constant parseValueAttribute() {
+        Constant result;
+        
+        try {
+            result = parseValue();
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a parameter annotation attribute.
+     * 
+     * @param visibility non-null; visibility of the parsed annotations
+     * @return non-null; the parsed list of lists of annotations
+     */
+    public AnnotationsList parseParameterAttribute(
+            AnnotationVisibility visibility) {
+        AnnotationsList result;
+        
+        try {
+            result = parseAnnotationsList(visibility);
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+    
+    /**
+     * Parses an annotation attribute, per se.
+     * 
+     * @param visibility non-null; visibility of the parsed annotations
+     * @return non-null; the list of annotations read from the attribute
+     * data
+     */
+    public Annotations parseAnnotationAttribute(
+            AnnotationVisibility visibility) {
+        Annotations result;
+        
+        try {
+            result = parseAnnotations(visibility);
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a list of annotation lists.
+     * 
+     * @param visibility non-null; visibility of the parsed annotations
+     * @return non-null; the list of annotation lists read from the attribute
+     * data
+     */
+    private AnnotationsList parseAnnotationsList(
+            AnnotationVisibility visibility) throws IOException {
+        int count = input.readUnsignedByte();
+
+        if (observer != null) {
+            parsed(1, "num_parameters: " + Hex.u1(count));
+        }
+
+        AnnotationsList outerList = new AnnotationsList(count);
+
+        for (int i = 0; i < count; i++) {
+            if (observer != null) {
+                parsed(0, "parameter_annotations[" + i + "]:");
+                changeIndent(1);
+            }
+
+            Annotations annotations = parseAnnotations(visibility);
+            outerList.set(i, annotations);
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        outerList.setImmutable();
+        return outerList;
+    }
+
+    /**
+     * Parses an annotation list.
+     * 
+     * @param visibility non-null; visibility of the parsed annotations
+     * @return non-null; the list of annotations read from the attribute
+     * data
+     */
+    private Annotations parseAnnotations(AnnotationVisibility visibility)
+            throws IOException {
+        int count = input.readUnsignedShort();
+
+        if (observer != null) {
+            parsed(2, "num_annotations: " + Hex.u2(count));
+        }
+
+        Annotations annotations = new Annotations();
+
+        for (int i = 0; i < count; i++) {
+            if (observer != null) {
+                parsed(0, "annotations[" + i + "]:");
+                changeIndent(1);
+            }
+
+            Annotation annotation = parseAnnotation(visibility);
+            annotations.add(annotation);
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        annotations.setImmutable();
+        return annotations;
+    }
+
+    /**
+     * Parses a single annotation.
+     * 
+     * @param visibility non-null; visibility of the parsed annotation
+     * @return non-null; the parsed annotation
+     */
+    private Annotation parseAnnotation(AnnotationVisibility visibility)
+            throws IOException {
+        requireLength(4);
+
+        int typeIndex = input.readUnsignedShort();
+        int numElements = input.readUnsignedShort();
+        CstUtf8 typeUtf8 = (CstUtf8) pool.get(typeIndex);
+        CstType type = new CstType(Type.intern(typeUtf8.getString()));
+
+        if (observer != null) {
+            parsed(2, "type: " + type.toHuman());
+            parsed(2, "num_elements: " + numElements);
+        }
+
+        Annotation annotation = new Annotation(type, visibility);
+
+        for (int i = 0; i < numElements; i++) {
+            if (observer != null) {
+                parsed(0, "elements[" + i + "]:");
+                changeIndent(1);
+            }
+
+            NameValuePair element = parseElement();
+            annotation.add(element);
+
+            if (observer != null) {
+                changeIndent(-1);
+            }
+        }
+
+        annotation.setImmutable();
+        return annotation;
+    }
+    
+    /**
+     * Parses a {@link NameValuePair}.
+     * 
+     * @return non-null; the parsed element
+     */
+    private NameValuePair parseElement() throws IOException {
+        requireLength(5);
+                
+        int elementNameIndex = input.readUnsignedShort();
+        CstUtf8 elementName = (CstUtf8) pool.get(elementNameIndex);
+                
+        if (observer != null) {
+            parsed(2, "element_name: " + elementName.toHuman());
+            parsed(0, "value: ");
+            changeIndent(1);
+        }
+
+        Constant value = parseValue();
+
+        if (observer != null) {
+            changeIndent(-1);
+        }
+
+        return new NameValuePair(elementName, value);
+    }
+
+    /**
+     * Parses an annotation value.
+     * 
+     * @return non-null; the parsed value
+     */
+    private Constant parseValue() throws IOException {
+        int tag = input.readUnsignedByte();
+
+        if (observer != null) {
+            CstUtf8 humanTag = new CstUtf8(Character.toString((char) tag));
+            parsed(1, "tag: " + humanTag.toQuoted());
+        }
+
+        switch (tag) {
+            case 'B': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstByte.make(value.getValue());
+            }
+            case 'C': {
+                CstInteger value = (CstInteger) parseConstant();
+                int intValue = value.getValue();
+                return CstChar.make(value.getValue());
+            }
+            case 'D': {
+                CstDouble value = (CstDouble) parseConstant();
+                return value;
+            }
+            case 'F': {
+                CstFloat value = (CstFloat) parseConstant();
+                return value;
+            }
+            case 'I': {
+                CstInteger value = (CstInteger) parseConstant();
+                return value;
+            }
+            case 'J': {
+                CstLong value = (CstLong) parseConstant();
+                return value;
+            }
+            case 'S': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstShort.make(value.getValue());
+            }
+            case 'Z': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstBoolean.make(value.getValue());
+            }
+            case 'c': {
+                int classInfoIndex = input.readUnsignedShort();
+                CstUtf8 value = (CstUtf8) pool.get(classInfoIndex);
+                Type type = Type.internReturnType(value.getString());
+                
+                if (observer != null) {
+                    parsed(2, "class_info: " + type.toHuman());
+                }
+
+                return new CstType(type);
+            }
+            case 's': {
+                CstString value = new CstString((CstUtf8) parseConstant());
+                return value;
+            }
+            case 'e': {
+                requireLength(4);
+
+                int typeNameIndex = input.readUnsignedShort();
+                int constNameIndex = input.readUnsignedShort();
+                CstUtf8 typeName = (CstUtf8) pool.get(typeNameIndex);
+                CstUtf8 constName = (CstUtf8) pool.get(constNameIndex);
+                
+                if (observer != null) {
+                    parsed(2, "type_name: " + typeName.toHuman());
+                    parsed(2, "const_name: " + constName.toHuman());
+                }
+
+                return new CstEnumRef(new CstNat(constName, typeName));
+            }
+            case '@': {
+                Annotation annotation =
+                    parseAnnotation(AnnotationVisibility.EMBEDDED);
+                return new CstAnnotation(annotation);
+            }
+            case '[': {
+                requireLength(2);
+
+                int numValues = input.readUnsignedShort();
+                CstArray.List list = new CstArray.List(numValues);
+
+                if (observer != null) {
+                    parsed(2, "num_values: " + numValues);
+                    changeIndent(1);
+                }
+
+                for (int i = 0; i < numValues; i++) {
+                    if (observer != null) {
+                        changeIndent(-1);
+                        parsed(0, "element_value[" + i + "]:");
+                        changeIndent(1);
+                    }
+                    list.set(i, parseValue());
+                }
+
+                if (observer != null) {
+                    changeIndent(-1);
+                }
+
+                list.setImmutable();
+                return new CstArray(list);
+            }
+            default: {
+                throw new ParseException("unknown annotation tag: " +
+                        Hex.u1(tag));
+            }
+        }
+    }
+    
+    /**
+     * Helper for {@link #parseValue}, which parses a constant reference
+     * and returns the referred-to constant value.
+     * 
+     * @return non-null; the parsed value
+     */
+    private Constant parseConstant() throws IOException {
+        int constValueIndex = input.readUnsignedShort();
+        Constant value = (Constant) pool.get(constValueIndex);
+
+        if (observer != null) {
+            String human = (value instanceof CstUtf8) 
+                ? ((CstUtf8) value).toQuoted() 
+                : value.toHuman();
+            parsed(2, "constant_value: " + human);
+        }
+
+        return value;
+    }
+
+    /**
+     * Helper which will throw an exception if the given number of bytes
+     * is not available to be read.
+     * 
+     * @param requiredLength the number of required bytes
+     */
+    private void requireLength(int requiredLength) throws IOException {
+        if (input.available() < requiredLength) {
+            throw new ParseException("truncated annotation attribute");
+        }
+    }
+    
+    /**
+     * Helper which indicates that some bytes were just parsed. This should
+     * only be used (for efficiency sake) if the parse is known to be
+     * observed.
+     * 
+     * @param length &gt;= 0; number of bytes parsed
+     * @param message non-null; associated message
+     */
+    private void parsed(int length, String message) {
+        observer.parsed(bytes, parseCursor, length, message);
+        parseCursor += length;
+    }
+
+    /**
+     * Convenience wrapper that simply calls through to
+     * <code>observer.changeIndent()</code>.
+     * 
+     * @param indent the amount to change the indent by
+     */
+    private void changeIndent(int indent) {
+        observer.changeIndent(indent);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeFactory.java b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
new file mode 100644
index 0000000..420d741
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.RawAttribute;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Factory capable of instantiating various {@link Attribute} subclasses
+ * depending on the context and name.
+ */
+public class AttributeFactory {
+    /** context for attributes on class files */
+    public static final int CTX_CLASS = 0;
+
+    /** context for attributes on fields */
+    public static final int CTX_FIELD = 1;
+
+    /** context for attributes on methods */
+    public static final int CTX_METHOD = 2;
+
+    /** context for attributes on code attributes */
+    public static final int CTX_CODE = 3;
+
+    /** number of contexts */
+    public static final int CTX_COUNT = 4;
+
+    /**
+     * Constructs an instance.
+     */
+    public AttributeFactory() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Parses and makes an attribute based on the bytes at the
+     * indicated position in the given array. This method figures out
+     * the name, and then does all the setup to call on to {@link #parse0},
+     * which does the actual construction.
+     * 
+     * @param cf non-null; class file to parse from
+     * @param context context to parse in; one of the <code>CTX_*</code>
+     * constants
+     * @param offset offset into <code>dcf</code>'s <code>bytes</code>
+     * to start parsing at
+     * @param observer null-ok; parse observer to report to, if any
+     * @return non-null; an appropriately-constructed {@link Attribute}
+     */
+    public final Attribute parse(DirectClassFile cf, int context, int offset,
+                                 ParseObserver observer) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if ((context < 0) || (context >= CTX_COUNT)) {
+            throw new IllegalArgumentException("bad context");
+        }
+
+        CstUtf8 name = null;
+
+        try {
+            ByteArray bytes = cf.getBytes();
+            ConstantPool pool = cf.getConstantPool();
+            int nameIdx = bytes.getUnsignedShort(offset);
+            int length = bytes.getInt(offset + 2);
+
+            name = (CstUtf8) pool.get(nameIdx);
+
+            if (observer != null) {
+                observer.parsed(bytes, offset, 2,
+                                "name: " + name.toHuman());
+                observer.parsed(bytes, offset + 2, 4,
+                                "length: " + Hex.u4(length));
+            }
+
+            return parse0(cf, context, name.getString(), offset + 6, length,
+                          observer);
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing " + 
+                    ((name != null) ? (name.toHuman() + " ") : "") +
+                    "attribute at offset " + Hex.u4(offset));
+            throw ex;
+        }
+    }
+
+    /**
+     * Parses attribute content. The base class implements this by constructing
+     * an instance of {@link RawAttribute}. Subclasses are expected to
+     * override this to do something better in most cases.
+     * 
+     * @param cf non-null; class file to parse from
+     * @param context context to parse in; one of the <code>CTX_*</code>
+     * constants
+     * @param name non-null; the attribute name
+     * @param offset offset into <code>bytes</code> to start parsing at; this
+     * is the offset to the start of attribute data, not to the header
+     * @param length the length of the attribute data
+     * @param observer null-ok; parse observer to report to, if any
+     * @return non-null; an appropriately-constructed {@link Attribute}
+     */
+    protected Attribute parse0(DirectClassFile cf, int context, String name,
+                               int offset, int length,
+                               ParseObserver observer) {
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        Attribute result = new RawAttribute(name, bytes, offset, length, pool);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, length, "attribute data");
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeListParser.java b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
new file mode 100644
index 0000000..7652265
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of attributes.
+ */
+final /*package*/ class AttributeListParser {
+    /** non-null; the class file to parse from */
+    private final DirectClassFile cf;
+
+    /** attribute parsing context */
+    private final int context;
+
+    /** offset in the byte array of the classfile to the start of the list */
+    private final int offset;
+
+    /** non-null; attribute factory to use */
+    private final AttributeFactory attributeFactory;
+
+    /** non-null; list of parsed attributes */
+    private final StdAttributeList list;
+
+    /** &gt;= -1; the end offset of this list in the byte array of the
+     * classfile, or <code>-1</code> if not yet parsed */
+    private int endOffset;
+
+    /** null-ok; parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf non-null; class file to parse from
+     * @param context attribute parsing context (see {@link AttributeFactory})
+     * @param offset offset in <code>bytes</code> to the start of the list
+     * @param attributeFactory non-null; attribute factory to use
+     */
+    public AttributeListParser(DirectClassFile cf, int context, int offset,
+                               AttributeFactory attributeFactory) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        int size = cf.getBytes().getUnsignedShort(offset);
+
+        this.cf = cf;
+        this.context = context;
+        this.offset = offset;
+        this.attributeFactory = attributeFactory;
+        this.list = new StdAttributeList(size);
+        this.endOffset = -1;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer null-ok; the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the <code>byte[]</code>
+     * which it came from.
+     *
+     * @return &gt;= 0; the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Gets the parsed list.
+     *
+     * @return non-null; the list
+     */
+    public StdAttributeList getList() {
+        parseIfNecessary();
+        return list;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        int sz = list.size();
+        int at = offset + 2; // Skip the count.
+
+        ByteArray bytes = cf.getBytes();
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "attributes_count: " + Hex.u2(sz));
+        }
+
+        for (int i = 0; i < sz; i++) {
+            try {
+                if (observer != null) {
+                    observer.parsed(bytes, at, 0,
+                                    "\nattributes[" + i + "]:\n");
+                    observer.changeIndent(1);
+                }
+
+                Attribute attrib =
+                    attributeFactory.parse(cf, context, at, observer);
+
+                at += attrib.byteLength();
+                list.set(i, attrib);
+
+                if (observer != null) {
+                    observer.changeIndent(-1);
+                    observer.parsed(bytes, at, 0,
+                                    "end attributes[" + i + "]\n");
+                }
+            } catch (ParseException ex) {
+                ex.addContext("...while parsing attributes[" + i + "]");
+                throw ex;
+            } catch (RuntimeException ex) {
+                ParseException pe = new ParseException(ex);
+                pe.addContext("...while parsing attributes[" + i + "]");
+                throw pe;
+            }
+        }
+
+        endOffset = at;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
new file mode 100644
index 0000000..d302349
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.util.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Opens all the class files found in a class path element. Path elements
+ * can point to class files, {jar,zip,apk} files, or directories containing
+ * class files.
+ */
+public class ClassPathOpener {
+
+    /** non-null; pathname to start with */
+    private final String pathname;
+    /** non-null; callback interface */
+    private final Consumer consumer;
+    /**
+     * If true, sort such that classes appear before their inner
+     * classes and "package-info" occurs before all other classes in that
+     * package.
+     */
+    private final boolean sort;
+
+    /**
+     * Callback interface for <code>ClassOpener</code>.
+     */
+    public interface Consumer {
+
+        /**
+         * Provides the file name and byte array for a class path element.
+         *
+         * @param name non-null; filename of element. May not be a valid
+         * filesystem path.
+         *
+         * @param bytes non-null; file data
+         * @return true on success. Result is or'd with all other results
+         * from <code>processFileBytes</code> and returned to the caller
+         * of <code>process()</code>.
+         */
+        boolean processFileBytes(String name, byte[] bytes);
+
+        /**
+         * Informs consumer that an exception occurred while processing
+         * this path element. Processing will continue if possible.
+         *
+         * @param ex non-null; exception
+         */
+        void onException(Exception ex);
+
+        /**
+         * Informs consumer that processing of an archive file has begun.
+         *
+         * @param file non-null; archive file being processed
+         */
+        void onProcessArchiveStart(File file);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param pathname non-null; path element to process
+     * @param sort if true, sort such that classes appear before their inner
+     * classes and "package-info" occurs before all other classes in that
+     * package.
+     * @param consumer non-null; callback interface
+     */
+    public ClassPathOpener(String pathname, boolean sort, Consumer consumer) {
+        this.pathname = pathname;
+        this.sort = sort;
+        this.consumer = consumer;
+    }
+
+    /**
+     * Processes a path element.
+     *
+     * @return the OR of all return values
+     * from <code>Consumer.processFileBytes()</code>.
+     */
+    public boolean process() {
+        File file = new File(pathname);
+
+        return processOne(file, true);
+    }
+
+    /**
+     * Processes one file.
+     *
+     * @param file non-null; the file to process
+     * @param topLevel whether this is a top-level file (that is,
+     * specified directly on the commandline)
+     * @return whether any processing actually happened
+     */
+    private boolean processOne(File file, boolean topLevel) {
+        try {
+            if (file.isDirectory()) {
+                return processDirectory(file, topLevel);
+            }
+
+            String path = file.getPath();
+
+            if (path.endsWith(".zip") ||
+                    path.endsWith(".jar") ||
+                    path.endsWith(".apk")) {
+                return processArchive(file);
+            }
+
+            byte[] bytes = FileUtils.readFile(file);
+            return consumer.processFileBytes(path, bytes);
+        } catch (Exception ex) {
+            consumer.onException(ex);
+            return false;
+        }
+    }
+
+    /**
+     * Sorts java class names such that outer classes preceed their inner
+     * classes and "package-info" preceeds all other classes in its package.
+     *
+     * @param a non-null; first class name
+     * @param b non-null; second class name
+     * @return <code>compareTo()</code>-style result
+     */
+    private static int compareClassNames(String a, String b) {
+        // Ensure inner classes sort second
+        a = a.replace('$','0');
+        b = b.replace('$','0');
+
+        /*
+         * Assuming "package-info" only occurs at the end, ensures package-info
+         * sorts first.
+         */
+        a = a.replace("package-info", "");
+        b = b.replace("package-info", "");
+
+        return a.compareTo(b);
+    }
+
+    /**
+     * Processes a directory recursively.
+     *
+     * @param dir non-null; file representing the directory
+     * @param topLevel whether this is a top-level directory (that is,
+     * specified directly on the commandline)
+     * @return whether any processing actually happened
+     */
+    private boolean processDirectory(File dir, boolean topLevel) {
+        if (topLevel) {
+            dir = new File(dir, ".");
+        }
+
+        File[] files = dir.listFiles();
+        int len = files.length;
+        boolean any = false;
+
+        if (sort) {
+            Arrays.sort(files, new Comparator<File>() {
+                public int compare(File a, File b) {
+                    return compareClassNames(a.getName(), b.getName());
+                }
+            });
+        }
+
+        for (int i = 0; i < len; i++) {
+            any |= processOne(files[i], false);
+        }
+
+        return any;
+    }
+
+    /**
+     * Processes the contents of an archive (<code>.zip</code>,
+     * <code>.jar</code>, or <code>.apk</code>).
+     *
+     * @param file non-null; archive file to process
+     * @return whether any processing actually happened
+     * @throws IOException on i/o problem
+     */
+    private boolean processArchive(File file) throws IOException {
+        ZipFile zip = new ZipFile(file);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
+        byte[] buf = new byte[20000];
+        boolean any = false;
+
+        ArrayList<? extends java.util.zip.ZipEntry> entriesList 
+                = Collections.list(zip.entries());
+
+        if (sort) {
+            Collections.sort(entriesList, new Comparator<ZipEntry>() {
+               public int compare (ZipEntry a, ZipEntry b) {
+                   return compareClassNames(a.getName(), b.getName());
+               }
+            });
+        }
+
+        consumer.onProcessArchiveStart(file);
+
+        for (ZipEntry one: entriesList) {
+
+            if (one.isDirectory()) {
+                continue;
+            }
+
+            String path = one.getName();
+            InputStream in = zip.getInputStream(one);
+
+            baos.reset();
+            for (;;) {
+                int amt = in.read(buf);
+                if (amt < 0) {
+                    break;
+                }
+
+                baos.write(buf, 0, amt);
+            }
+
+            in.close();
+
+            byte[] bytes = baos.toByteArray();
+            any |= consumer.processFileBytes(path, bytes);
+        }
+
+        zip.close();
+        return any;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/CodeObserver.java b/dx/src/com/android/dx/cf/direct/CodeObserver.java
new file mode 100644
index 0000000..950147f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/CodeObserver.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.code.ByteOps;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.SwitchList;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Bytecode visitor to use when "observing" bytecode getting parsed.
+ */
+public class CodeObserver implements BytecodeArray.Visitor {
+    /** non-null; actual array of bytecode */
+    private final ByteArray bytes;
+
+    /** non-null; observer to inform of parsing */
+    private final ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; actual array of bytecode
+     * @param observer non-null; observer to inform of parsing
+     */
+    public CodeObserver(ByteArray bytes, ParseObserver observer) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (observer == null) {
+            throw new NullPointerException("observer == null");
+        }
+
+        this.bytes = bytes;
+        this.observer = observer;
+    }
+
+    /** {@inheritDoc} */
+    public void visitInvalid(int opcode, int offset, int length) {
+        observer.parsed(bytes, offset, length, header(offset));
+    }
+
+    /** {@inheritDoc} */
+    public void visitNoArgs(int opcode, int offset, int length, Type type) {
+        observer.parsed(bytes, offset, length, header(offset));
+    }
+
+    /** {@inheritDoc} */
+    public void visitLocal(int opcode, int offset, int length,
+            int idx, Type type, int value) {
+        String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
+        boolean argComment = (length == 1);
+        String valueStr = "";
+
+        if (opcode == ByteOps.IINC) {
+            valueStr = ", #" +
+                ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
+        }
+
+        String catStr = "";
+        if (type.isCategory2()) {
+            catStr = (argComment ? "," : " //") + " category-2";
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + (argComment ? " // " : " ") +
+                        idxStr + valueStr + catStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitConstant(int opcode, int offset, int length,
+            Constant cst, int value) {
+        if (cst instanceof CstKnownNull) {
+            // This is aconst_null.
+            visitNoArgs(opcode, offset, length, null);
+            return;
+        }
+
+        if (cst instanceof CstInteger) {
+            visitLiteralInt(opcode, offset, length, value);
+            return;
+        }
+
+        if (cst instanceof CstLong) {
+            visitLiteralLong(opcode, offset, length,
+                             ((CstLong) cst).getValue());
+            return;
+        }
+
+        if (cst instanceof CstFloat) {
+            visitLiteralFloat(opcode, offset, length,
+                              ((CstFloat) cst).getIntBits());
+            return;
+        }
+
+        if (cst instanceof CstDouble) {
+            visitLiteralDouble(opcode, offset, length,
+                             ((CstDouble) cst).getLongBits());
+            return;
+        }
+
+        String valueStr = "";
+        if (value != 0) {
+            valueStr = ", ";
+            if (opcode == ByteOps.MULTIANEWARRAY) {
+                valueStr += Hex.u1(value);
+            } else {
+                valueStr += Hex.u2(value);
+            }
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + " " + cst + valueStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitBranch(int opcode, int offset, int length,
+                            int target) {
+        String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
+        observer.parsed(bytes, offset, length,
+                        header(offset) + " " + targetStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitSwitch(int opcode, int offset, int length,
+            SwitchList cases, int padding) {
+        int sz = cases.size();
+        StringBuffer sb = new StringBuffer(sz * 20 + 100);
+
+        sb.append(header(offset));
+        if (padding != 0) {
+            sb.append(" // padding: " + Hex.u4(padding));
+        }
+        sb.append('\n');
+
+        for (int i = 0; i < sz; i++) {
+            sb.append("  ");
+            sb.append(Hex.s4(cases.getValue(i)));
+            sb.append(": ");
+            sb.append(Hex.u2(cases.getTarget(i)));
+            sb.append('\n');
+        }
+
+        sb.append("  default: ");
+        sb.append(Hex.u2(cases.getDefaultTarget()));
+
+        observer.parsed(bytes, offset, length, sb.toString());
+    }
+
+    /** {@inheritDoc} */
+    public void visitNewarray(int offset, int length, CstType cst,
+            ArrayList<Constant> intVals) {
+        String commentOrSpace = (length == 1) ? " // " : " ";
+        String typeName = cst.getClassType().getComponentType().toHuman();
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrSpace + typeName);
+    }
+
+    /** {@inheritDoc} */
+    public void setPreviousOffset(int offset) {
+        // Do nothing
+    }
+
+    /** {@inheritDoc} */
+    public int getPreviousOffset() {
+        return -1;
+    }
+
+    /**
+     * Helper to produce the first bit of output for each instruction.
+     * 
+     * @param offset the offset to the start of the instruction
+     */
+    private String header(int offset) {
+        /*
+         * Note: This uses the original bytecode, not the
+         * possibly-transformed one.
+         */
+        int opcode = bytes.getUnsignedByte(offset);
+        String name = ByteOps.opName(opcode);
+
+        if (opcode == ByteOps.WIDE) {
+            opcode = bytes.getUnsignedByte(offset + 1);
+            name += " " + ByteOps.opName(opcode);
+        }
+
+        return Hex.u2(offset) + ": " + name;
+    }
+
+    /**
+     * Helper for {code #visitConstant} where the constant is an
+     * <code>int</code>.
+     * 
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param value constant value
+     */
+    private void visitLiteralInt(int opcode, int offset, int length,
+            int value) {
+        String commentOrSpace = (length == 1) ? " // " : " ";
+        String valueStr;
+
+        opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
+        if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
+            valueStr = "#" + Hex.s1(value);
+        } else if (opcode == ByteOps.SIPUSH) {
+            valueStr = "#" + Hex.s2(value);
+        } else {
+            valueStr = "#" + Hex.s4(value);
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrSpace + valueStr);
+    }
+
+    /**
+     * Helper for {code #visitConstant} where the constant is a
+     * <code>long</code>.
+     * 
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param value constant value
+     */
+    private void visitLiteralLong(int opcode, int offset, int length,
+            long value) {
+        String commentOrLit = (length == 1) ? " // " : " #";
+        String valueStr;
+
+        if (length == 1) {
+            valueStr = Hex.s1((int) value);
+        } else {
+            valueStr = Hex.s8(value);
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrLit + valueStr);
+    }
+
+    /**
+     * Helper for {code #visitConstant} where the constant is a
+     * <code>float</code>.
+     * 
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param bits constant value, as float-bits
+     */
+    private void visitLiteralFloat(int opcode, int offset, int length,
+            int bits) {
+        String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + optArg + " // " +
+                        Float.intBitsToFloat(bits));
+    }
+
+    /**
+     * Helper for {code #visitConstant} where the constant is a
+     * <code>double</code>.
+     * 
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param bits constant value, as double-bits
+     */
+    private void visitLiteralDouble(int opcode, int offset, int length,
+            long bits) {
+        String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + optArg + " // " +
+                        Double.longBitsToDouble(bits));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
new file mode 100644
index 0000000..e4751a4
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.cst.ConstantPoolParser;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Class file with info taken from a <code>byte[]</code> or slice thereof.
+ */
+public class DirectClassFile implements ClassFile {
+    /** the expected value of the ClassFile.magic field */
+    private static final int CLASS_FILE_MAGIC = 0xcafebabe;
+
+    /**
+     * minimum <code>.class</code> file major version
+     * 
+     * The class file definition (vmspec/2nd-edition) says:
+     * 
+     *     "Implementations of version 1.2 of the
+     *     Java 2 platform can support class file
+     *     formats of versions in the range 45.0
+     *     through 46.0 inclusive."
+     * 
+     * The class files generated by the build are currently
+     * (as of 11/2006) reporting version 49.0 (0x31.0x00),
+     * however, so we use that as our upper bound.
+     * 
+     * Valid ranges are typically of the form
+     * "A.0 through B.C inclusive" where A <= B and C >= 0,
+     * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+     */
+    private static final int CLASS_FILE_MIN_MAJOR_VERSION = 45;
+
+    /** maximum <code>.class</code> file major version */
+    private static final int CLASS_FILE_MAX_MAJOR_VERSION = 50;
+
+    /** maximum <code>.class</code> file minor version */
+    private static final int CLASS_FILE_MAX_MINOR_VERSION = 0;
+
+    /**
+     * non-null; the file path for the class, excluding any base directory
+     * specification 
+     */
+    private final String filePath;
+
+    /** non-null; the bytes of the file */
+    private final ByteArray bytes;
+
+    /**
+     * whether to be strict about parsing; if
+     * <code>false</code>, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking) 
+     */
+    private final boolean strictParse;
+
+    /**
+     * null-ok; the constant pool; only ever <code>null</code>
+     * before the constant pool is successfully parsed 
+     */
+    private StdConstantPool pool;
+
+    /**
+     * the class file field <code>access_flags</code>; will be <code>-1</code>
+     * before the file is successfully parsed 
+     */
+    private int accessFlags;
+
+    /**
+     * null-ok; the class file field <code>this_class</code>,
+     * interpreted as a type constant; only ever <code>null</code>
+     * before the file is successfully parsed
+     */
+    private CstType thisClass;
+
+    /**
+     * null-ok; the class file field <code>super_class</code>, interpreted
+     * as a type constant if non-zero 
+     */
+    private CstType superClass;
+
+    /**
+     * null-ok; the class file field <code>interfaces</code>; only
+     * ever <code>null</code> before the file is successfully
+     * parsed 
+     */
+    private TypeList interfaces;
+
+    /**
+     * null-ok; the class file field <code>fields</code>; only ever
+     * <code>null</code> before the file is successfully parsed 
+     */
+    private FieldList fields;
+
+    /**
+     * null-ok; the class file field <code>methods</code>; only ever
+     * <code>null</code> before the file is successfully parsed 
+     */
+    private MethodList methods;
+
+    /**
+     * null-ok; the class file field <code>attributes</code>; only
+     * ever <code>null</code> before the file is successfully
+     * parsed 
+     */
+    private StdAttributeList attributes;
+
+    /** null-ok; attribute factory, if any */
+    private AttributeFactory attributeFactory;
+
+    /** null-ok; parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Returns the string form of an object or <code>"(none)"</code>
+     * (rather than <code>"null"</code>) for <code>null</code>.
+     * 
+     * @param obj null-ok; the object to stringify
+     * @return non-null; the appropriate string form
+     */
+    public static String stringOrNone(Object obj) {
+        if (obj == null) {
+            return "(none)";
+        }
+
+        return obj.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; the bytes of the file
+     * @param filePath non-null; the file path for the class,
+     * excluding any base directory specification
+     * @param strictParse whether to be strict about parsing; if
+     * <code>false</code>, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking)
+     */
+    public DirectClassFile(ByteArray bytes, String filePath,
+                           boolean strictParse) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (filePath == null) {
+            throw new NullPointerException("filePath == null");
+        }
+
+        this.filePath = filePath;
+        this.bytes = bytes;
+        this.strictParse = strictParse;
+        this.accessFlags = -1;
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; the bytes of the file
+     * @param filePath non-null; the file path for the class,
+     * excluding any base directory specification
+     * @param strictParse whether to be strict about parsing; if
+     * <code>false</code>, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking)
+     */
+    public DirectClassFile(byte[] bytes, String filePath,
+                           boolean strictParse) {
+        this(new ByteArray(bytes), filePath, strictParse);
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     * 
+     * @param observer null-ok; the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Sets the attribute factory to use.
+     * 
+     * @param attributeFactory non-null; the attribute factory
+     */
+    public void setAttributeFactory(AttributeFactory attributeFactory) {
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        this.attributeFactory = attributeFactory;
+    }
+
+    /**
+     * Gets the {@link ByteArray} that this instance's data comes from.
+     * 
+     * @return non-null; the bytes
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /** {@inheritDoc} */
+    public int getMagic() {
+        parseToInterfacesIfNecessary();
+        return getMagic0();
+    }
+
+    /** {@inheritDoc} */
+    public int getMinorVersion() {
+        parseToInterfacesIfNecessary();
+        return getMinorVersion0();
+    }
+
+    /** {@inheritDoc} */
+    public int getMajorVersion() {
+        parseToInterfacesIfNecessary();
+        return getMajorVersion0();
+    }
+
+    /** {@inheritDoc} */
+    public int getAccessFlags() {
+        parseToInterfacesIfNecessary();
+        return accessFlags;
+    }
+
+    /** {@inheritDoc} */
+    public CstType getThisClass() {
+        parseToInterfacesIfNecessary();
+        return thisClass;
+    }
+
+    /** {@inheritDoc} */
+    public CstType getSuperclass() {
+        parseToInterfacesIfNecessary();
+        return superClass;
+    }
+
+    /** {@inheritDoc} */
+    public ConstantPool getConstantPool() {
+        parseToInterfacesIfNecessary();
+        return pool;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList getInterfaces() {
+        parseToInterfacesIfNecessary();
+        return interfaces;
+    }
+
+    /** {@inheritDoc} */
+    public FieldList getFields() {
+        parseToEndIfNecessary();
+        return fields;
+    }
+
+    /** {@inheritDoc} */
+    public MethodList getMethods() {
+        parseToEndIfNecessary();
+        return methods;
+    }
+
+    /** {@inheritDoc} */
+    public AttributeList getAttributes() {
+        parseToEndIfNecessary();
+        return attributes;
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getSourceFile() {
+        AttributeList attribs = getAttributes();
+        Attribute attSf = attribs.findFirst(AttSourceFile.ATTRIBUTE_NAME);
+
+        if (attSf instanceof AttSourceFile) {
+            return ((AttSourceFile) attSf).getSourceFile();
+        }
+
+        return null;
+    }
+
+    /**
+     * Constructs and returns an instance of {@link TypeList} whose
+     * data comes from the bytes of this instance, interpreted as a
+     * list of constant pool indices for classes, which are in turn
+     * translated to type constants. Instance construction will fail
+     * if any of the (alleged) indices turn out not to refer to
+     * constant pool entries of type <code>Class</code>.
+     * 
+     * @param offset offset into {@link #bytes} for the start of the
+     * data
+     * @param size number of elements in the list (not number of bytes)
+     * @return non-null; an appropriately-constructed class list
+     */
+    public TypeList makeTypeList(int offset, int size) {
+        if (size == 0) {
+            return StdTypeList.EMPTY;
+        }
+
+        if (pool == null) {
+            throw new IllegalStateException("pool not yet initialized");
+        }
+        
+        return new DcfTypeList(bytes, offset, size, pool, observer);
+    }
+
+    /**
+     * Gets the class file field <code>magic</code>, but without doing any
+     * checks or parsing first.
+     * 
+     * @return the magic value
+     */
+    public int getMagic0() {
+        return bytes.getInt(0);
+    }
+
+    /**
+     * Gets the class file field <code>minor_version</code>, but
+     * without doing any checks or parsing first.
+     * 
+     * @return the minor version
+     */
+    public int getMinorVersion0() {
+        return bytes.getUnsignedShort(4);
+    }
+
+    /**
+     * Gets the class file field <code>major_version</code>, but
+     * without doing any checks or parsing first.
+     * 
+     * @return the major version
+     */
+    public int getMajorVersion0() {
+        return bytes.getUnsignedShort(6);
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run to cover up to
+     * the interfaces list.
+     */
+    private void parseToInterfacesIfNecessary() {
+        if (accessFlags == -1) {
+            parse();
+        }
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseToEndIfNecessary() {
+        if (attributes == null) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the parsing, handing exceptions.
+     */
+    private void parse() {
+        try {
+            parse0();
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing " + filePath);
+            throw ex;
+        } catch (RuntimeException ex) {
+            ParseException pe = new ParseException(ex);
+            pe.addContext("...while parsing " + filePath);
+            throw pe;
+        }
+    }
+
+    /**
+     * Sees if the .class file header magic/version are within
+     * range.
+     * 
+     * @param magic the value of a classfile "magic" field
+     * @param minorVersion the value of a classfile "minor_version" field
+     * @param majorVersion the value of a classfile "major_version" field
+     * @return true iff the parameters are valid and within range
+     */
+    private boolean isGoodVersion(int magic, int minorVersion,
+            int majorVersion) {
+        /* Valid version ranges are typically of the form
+         * "A.0 through B.C inclusive" where A <= B and C >= 0,
+         * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+         */
+        if (magic == CLASS_FILE_MAGIC && minorVersion >= 0) {
+            /* Check against max first to handle the case where
+             * MIN_MAJOR == MAX_MAJOR.
+             */
+            if (majorVersion == CLASS_FILE_MAX_MAJOR_VERSION) {
+                if (minorVersion <= CLASS_FILE_MAX_MINOR_VERSION) {
+                    return true;
+                }
+            } else if (majorVersion < CLASS_FILE_MAX_MAJOR_VERSION &&
+                       majorVersion >= CLASS_FILE_MIN_MAJOR_VERSION) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse0() {
+        if (bytes.size() < 10) {
+            throw new ParseException("severely truncated class file");
+        }
+
+        if (observer != null) {
+            observer.parsed(bytes, 0, 0, "begin classfile");
+            observer.parsed(bytes, 0, 4, "magic: " + Hex.u4(getMagic0()));
+            observer.parsed(bytes, 4, 2,
+                            "minor_version: " + Hex.u2(getMinorVersion0()));
+            observer.parsed(bytes, 6, 2,
+                            "major_version: " + Hex.u2(getMajorVersion0()));
+        }
+
+        if (strictParse) {
+            /* Make sure that this looks like a valid class file with a
+             * version that we can handle.
+             */
+            if (!isGoodVersion(getMagic0(), getMinorVersion0(),
+                               getMajorVersion0())) {
+                throw new ParseException("bad class file magic (" +
+                                         Hex.u4(getMagic0()) +
+                                         ") or version (" +
+                                         Hex.u2(getMajorVersion0()) + "." +
+                                         Hex.u2(getMinorVersion0()) + ")");
+            }
+        }
+
+        ConstantPoolParser cpParser = new ConstantPoolParser(bytes);
+        cpParser.setObserver(observer);
+        pool = cpParser.getPool();
+        pool.setImmutable();
+
+        int at = cpParser.getEndOffset();
+        int accessFlags = bytes.getUnsignedShort(at); // u2 access_flags;
+        int cpi = bytes.getUnsignedShort(at + 2); // u2 this_class;
+        thisClass = (CstType) pool.get(cpi);
+        cpi = bytes.getUnsignedShort(at + 4); // u2 super_class;
+        superClass = (CstType) pool.get0Ok(cpi);
+        int count = bytes.getUnsignedShort(at + 6); // u2 interfaces_count
+
+        if (observer != null) {
+            observer.parsed(bytes, at, 2,
+                            "access_flags: " + 
+                            AccessFlags.classString(accessFlags));
+            observer.parsed(bytes, at + 2, 2, "this_class: " + thisClass);
+            observer.parsed(bytes, at + 4, 2, "super_class: " +
+                            stringOrNone(superClass));
+            observer.parsed(bytes, at + 6, 2,
+                            "interfaces_count: " + Hex.u2(count));
+            if (count != 0) {
+                observer.parsed(bytes, at + 8, 0, "interfaces:");
+            }
+        }
+
+        at += 8;
+        interfaces = makeTypeList(at, count);
+        at += count * 2;
+
+        if (strictParse) {
+            /*
+             * Make sure that the file/jar path matches the declared
+             * package/class name.
+             */
+            String thisClassName = thisClass.getClassType().getClassName();
+            if (!(filePath.endsWith(".class") &&
+                  filePath.startsWith(thisClassName) &&
+                  (filePath.length() == (thisClassName.length() + 6)))) {
+                throw new ParseException("class name (" + thisClassName +
+                                         ") does not match path (" +
+                                         filePath + ")");
+            }
+        }
+
+        /*
+         * Only set the instance variable accessFlags here, since
+         * that's what signals a successful parse of the first part of
+         * the file (through the interfaces list).
+         */
+        this.accessFlags = accessFlags;
+
+        FieldListParser flParser =
+            new FieldListParser(this, thisClass, at, attributeFactory);
+        flParser.setObserver(observer);
+        fields = flParser.getList();
+        at = flParser.getEndOffset();
+
+        MethodListParser mlParser =
+            new MethodListParser(this, thisClass, at, attributeFactory);
+        mlParser.setObserver(observer);
+        methods = mlParser.getList();
+        at = mlParser.getEndOffset();
+
+        AttributeListParser alParser =
+            new AttributeListParser(this, AttributeFactory.CTX_CLASS, at,
+                                    attributeFactory);
+        alParser.setObserver(observer);
+        attributes = alParser.getList();
+        attributes.setImmutable();
+        at = alParser.getEndOffset();
+
+        if (at != bytes.size()) {
+            throw new ParseException("extra bytes at end of class file, " +
+                                     "at offset " + Hex.u4(at));
+        }
+
+        if (observer != null) {
+            observer.parsed(bytes, at, 0, "end classfile");
+        }
+    }
+
+    /**
+     * Implementation of {@link TypeList} whose data comes directly
+     * from the bytes of an instance of this (outer) class,
+     * interpreted as a list of constant pool indices for classes
+     * which are in turn returned as type constants. Instance
+     * construction will fail if any of the (alleged) indices turn out
+     * not to refer to constant pool entries of type
+     * <code>Class</code>.
+     */
+    private static class DcfTypeList implements TypeList {
+        /** non-null; array containing the data */
+        private final ByteArray bytes;
+        
+        /** number of elements in the list (not number of bytes) */
+        private final int size;
+
+        /** non-null; the constant pool */
+        private final StdConstantPool pool;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param bytes non-null; original classfile's bytes
+         * @param offset offset into {@link #bytes} for the start of the
+         * data
+         * @param size number of elements in the list (not number of bytes)
+         * @param pool non-null; the constant pool to use
+         * @param observer null-ok; parse observer to use, if any
+         */
+        public DcfTypeList(ByteArray bytes, int offset, int size,
+                StdConstantPool pool, ParseObserver observer) {
+            if (size < 0) {
+                throw new IllegalArgumentException("size < 0");
+            }
+
+            bytes = bytes.slice(offset, offset + size * 2);
+            this.bytes = bytes;
+            this.size = size;
+            this.pool = pool;
+
+            for (int i = 0; i < size; i++) {
+                offset = i * 2;
+                int idx = bytes.getUnsignedShort(offset);
+                CstType type;
+                try {
+                    type = (CstType) pool.get(idx);
+                } catch (ClassCastException ex) {
+                    // Translate the exception.
+                    throw new RuntimeException("bogus class cpi", ex);
+                }
+                if (observer != null) {
+                    observer.parsed(bytes, offset, 2, "  " + type);
+                }
+            }
+        }
+
+        /** {@inheritDoc} */
+        public boolean isMutable() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        public int size() {
+            return size;
+        }
+
+        /** {@inheritDoc} */
+        public int getWordCount() {
+            // It is the same as size because all elements are classes.
+            return size;
+        }
+
+        /** {@inheritDoc} */
+        public Type getType(int n) {
+            int idx = bytes.getUnsignedShort(n * 2);
+            return ((CstType) pool.get(idx)).getClassType();
+        }
+
+        /** {@inheritDoc} */
+        public TypeList withAddedType(Type type) {
+            throw new UnsupportedOperationException("unsupported");
+        }        
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/FieldListParser.java b/dx/src/com/android/dx/cf/direct/FieldListParser.java
new file mode 100644
index 0000000..06dcc41
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/FieldListParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdField;
+import com.android.dx.cf.iface.StdFieldList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of fields in a class file.
+ */
+final /*package*/ class FieldListParser extends MemberListParser {
+    /** non-null; list in progress */
+    private final StdFieldList fields;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param cf non-null; the class file to parse from
+     * @param definer non-null; class being defined
+     * @param offset offset in <code>bytes</code> to the start of the list
+     * @param attributeFactory non-null; attribute factory to use
+     */
+    public FieldListParser(DirectClassFile cf, CstType definer, int offset,
+            AttributeFactory attributeFactory) {
+        super(cf, definer, offset, attributeFactory);
+        fields = new StdFieldList(getCount());
+    }
+
+    /**
+     * Gets the parsed list.
+     * 
+     * @return non-null; the parsed list
+     */
+    public StdFieldList getList() {
+        parseIfNecessary();
+        return fields;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanName() {
+        return "field";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanAccessFlags(int accessFlags) {
+        return AccessFlags.fieldString(accessFlags);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getAttributeContext() {
+        return AttributeFactory.CTX_FIELD;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Member set(int n, int accessFlags, CstNat nat,
+                         AttributeList attributes) {
+        StdField field =
+            new StdField(getDefiner(), accessFlags, nat, attributes);
+
+        fields.set(n, field);
+        return field;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MemberListParser.java b/dx/src/com/android/dx/cf/direct/MemberListParser.java
new file mode 100644
index 0000000..3c0bfa8
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MemberListParser.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of class file members (that is, fields and methods).
+ */
+abstract /*package*/ class MemberListParser {
+    /** non-null; the class file to parse from */
+    private final DirectClassFile cf;
+
+    /** non-null; class being defined */
+    private final CstType definer;
+
+    /** offset in the byte array of the classfile to the start of the list */
+    private final int offset;
+
+    /** non-null; attribute factory to use */
+    private final AttributeFactory attributeFactory;
+
+    /** &gt;= -1; the end offset of this list in the byte array of the
+     * classfile, or <code>-1</code> if not yet parsed */
+    private int endOffset;
+
+    /** null-ok; parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf non-null; the class file to parse from
+     * @param definer non-null; class being defined
+     * @param offset offset in <code>bytes</code> to the start of the list
+     * @param attributeFactory non-null; attribute factory to use
+     */
+    public MemberListParser(DirectClassFile cf, CstType definer,
+            int offset, AttributeFactory attributeFactory) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if (offset < 0) {
+            throw new IllegalArgumentException("offset < 0");
+        }
+
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        this.cf = cf;
+        this.definer = definer;
+        this.offset = offset;
+        this.attributeFactory = attributeFactory;
+        this.endOffset = -1;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the <code>byte[]</code>
+     * which it came from.
+     *
+     * @return &gt;= 0; the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer null-ok; the observer
+     */
+    public final void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    protected final void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Gets the count of elements in the list.
+     *
+     * @return the count
+     */
+    protected final int getCount() {
+        ByteArray bytes = cf.getBytes();
+        return bytes.getUnsignedShort(offset);
+    }
+
+    /**
+     * Gets the class file being defined.
+     *
+     * @return non-null; the class
+     */
+    protected final CstType getDefiner() {
+        return definer;
+    }
+
+    /**
+     * Gets the human-oriented name for what this instance is parsing.
+     * Subclasses must override this method.
+     * 
+     * @return non-null; the human oriented name
+     */
+    protected abstract String humanName();
+
+    /**
+     * Gets the human-oriented string for the given access flags.
+     * Subclasses must override this method.
+     *
+     * @param accessFlags the flags
+     * @return non-null; the string form
+     */
+    protected abstract String humanAccessFlags(int accessFlags);
+
+    /**
+     * Gets the <code>CTX_*</code> constant to use when parsing attributes.
+     * Subclasses must override this method.
+     * 
+     * @return non-null; the human oriented name
+     */
+    protected abstract int getAttributeContext();
+
+    /**
+     * Sets an element in the list. Subclasses must override this method.
+     *
+     * @param n which element
+     * @param accessFlags the <code>access_flags</code>
+     * @param nat the interpreted name and type (based on the two
+     * <code>*_index</code> fields)
+     * @param attributes list of parsed attributes
+     * @return non-null; the constructed member
+     */
+    protected abstract Member set(int n, int accessFlags, CstNat nat,
+            AttributeList attributes);
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        int attributeContext = getAttributeContext();
+        int count = getCount();
+        int at = offset + 2; // Skip the count.
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            humanName() + "s_count: " + Hex.u2(count));
+        }
+
+        for (int i = 0; i < count; i++) {
+            try {
+                int accessFlags = bytes.getUnsignedShort(at);
+                int nameIdx = bytes.getUnsignedShort(at + 2);
+                int descIdx = bytes.getUnsignedShort(at + 4);
+                CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+                CstUtf8 desc = (CstUtf8) pool.get(descIdx);
+
+                if (observer != null) {
+                    observer.startParsingMember(bytes, at, name.getString(),
+                                                desc.getString());
+                    observer.parsed(bytes, at, 0, "\n" + humanName() +
+                                    "s[" + i + "]:\n");
+                    observer.changeIndent(1);
+                    observer.parsed(bytes, at, 2,
+                                    "access_flags: " +
+                                    humanAccessFlags(accessFlags));
+                    observer.parsed(bytes, at + 2, 2,
+                                    "name: " + name.toHuman());
+                    observer.parsed(bytes, at + 4, 2,
+                                    "descriptor: " + desc.toHuman());
+                }
+
+                at += 6;
+                AttributeListParser parser =
+                    new AttributeListParser(cf, attributeContext, at,
+                                            attributeFactory);
+                parser.setObserver(observer);
+                at = parser.getEndOffset();
+                StdAttributeList attributes = parser.getList();
+                attributes.setImmutable();
+                CstNat nat = new CstNat(name, desc);
+                Member member = set(i, accessFlags, nat, attributes);
+
+                if (observer != null) {
+                    observer.changeIndent(-1);
+                    observer.parsed(bytes, at, 0, "end " + humanName() +
+                                    "s[" + i + "]\n");
+                    observer.endParsingMember(bytes, at, name.getString(),
+                                              desc.getString(), member);
+                }
+            } catch (ParseException ex) {
+                ex.addContext("...while parsing " + humanName() + "s[" + i +
+                              "]");
+                throw ex;
+            } catch (RuntimeException ex) {
+                ParseException pe = new ParseException(ex);
+                pe.addContext("...while parsing " + humanName() + "s[" + i +
+                              "]");
+                throw pe;
+            }
+        }
+
+        endOffset = at;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MethodListParser.java b/dx/src/com/android/dx/cf/direct/MethodListParser.java
new file mode 100644
index 0000000..9ca8ba6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MethodListParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdMethod;
+import com.android.dx.cf.iface.StdMethodList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of methods in a class file.
+ */
+final /*package*/ class MethodListParser extends MemberListParser {
+    /** non-null; list in progress */
+    final private StdMethodList methods;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param cf non-null; the class file to parse from
+     * @param definer non-null; class being defined
+     * @param offset offset in <code>bytes</code> to the start of the list
+     * @param attributeFactory non-null; attribute factory to use
+     */
+    public MethodListParser(DirectClassFile cf, CstType definer,
+            int offset, AttributeFactory attributeFactory) {
+        super(cf, definer, offset, attributeFactory);
+        methods = new StdMethodList(getCount());
+    }
+
+    /**
+     * Gets the parsed list.
+     * 
+     * @return non-null; the parsed list
+     */
+    public StdMethodList getList() {
+        parseIfNecessary();
+        return methods;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanName() {
+        return "method";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanAccessFlags(int accessFlags) {
+        return AccessFlags.methodString(accessFlags);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getAttributeContext() {
+        return AttributeFactory.CTX_METHOD;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Member set(int n, int accessFlags, CstNat nat,
+                         AttributeList attributes) {
+        StdMethod meth =
+            new StdMethod(getDefiner(), accessFlags, nat, attributes);
+
+        methods.set(n, meth);
+        return meth;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
new file mode 100644
index 0000000..ab0e2f7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.cf.attrib.AttDeprecated;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.attrib.AttSynthetic;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Standard subclass of {@link AttributeFactory}, which knows how to parse
+ * all the standard attribute types.
+ */
+public class StdAttributeFactory
+    extends AttributeFactory {
+    /** non-null; shared instance of this class */
+    public static final StdAttributeFactory THE_ONE =
+        new StdAttributeFactory();
+
+    /**
+     * Constructs an instance.
+     */
+    public StdAttributeFactory() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Attribute parse0(DirectClassFile cf, int context, String name,
+            int offset, int length, ParseObserver observer) {
+        switch (context) {
+            case CTX_CLASS: {
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttEnclosingMethod.ATTRIBUTE_NAME) {
+                    return enclosingMethod(cf, offset, length, observer);
+                }
+                if (name == AttInnerClasses.ATTRIBUTE_NAME) {
+                    return innerClasses(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSourceFile.ATTRIBUTE_NAME) {
+                    return sourceFile(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_FIELD: {
+                if (name == AttConstantValue.ATTRIBUTE_NAME) {
+                    return constantValue(cf, offset, length, observer);
+                }
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_METHOD: {
+                if (name == AttAnnotationDefault.ATTRIBUTE_NAME) {
+                    return annotationDefault(cf, offset, length, observer);
+                }
+                if (name == AttCode.ATTRIBUTE_NAME) {
+                    return code(cf, offset, length, observer);
+                }
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttExceptions.ATTRIBUTE_NAME) {
+                    return exceptions(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeInvisibleParameterAnnotations.
+                        ATTRIBUTE_NAME) {
+                    return runtimeInvisibleParameterAnnotations(
+                            cf, offset, length, observer);
+                }
+                if (name == AttRuntimeVisibleParameterAnnotations.
+                        ATTRIBUTE_NAME) {
+                    return runtimeVisibleParameterAnnotations(
+                            cf, offset, length, observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_CODE: {
+                if (name == AttLineNumberTable.ATTRIBUTE_NAME) {
+                    return lineNumberTable(cf, offset, length, observer);
+                }
+                if (name == AttLocalVariableTable.ATTRIBUTE_NAME) {
+                    return localVariableTable(cf, offset, length, observer);
+                }
+                if (name == AttLocalVariableTypeTable.ATTRIBUTE_NAME) {
+                    return localVariableTypeTable(cf, offset, length,
+                            observer);
+                }
+                break;
+            }
+        }
+
+        return super.parse0(cf, context, name, offset, length, observer);
+    }
+
+    /**
+     * Parses an <code>AnnotationDefault</code> attribute.
+     */
+    private Attribute annotationDefault(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Constant cst = ap.parseValueAttribute();
+
+        return new AttAnnotationDefault(cst, length);
+    }
+
+    /**
+     * Parses a <code>Code</code> attribute.
+     */
+    private Attribute code(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 12) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack
+        int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals
+        int codeLength = bytes.getInt(offset + 4); // u4 code_length
+        int origOffset = offset;
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "max_stack: " + Hex.u2(maxStack));
+            observer.parsed(bytes, offset + 2, 2,
+                            "max_locals: " + Hex.u2(maxLocals));
+            observer.parsed(bytes, offset + 4, 4,
+                            "code_length: " + Hex.u4(codeLength));
+        }
+
+        offset += 8;
+        length -= 8;
+
+        if (length < (codeLength + 4)) {
+            return throwTruncated();
+        }
+
+        int codeOffset = offset;
+        offset += codeLength;
+        length -= codeLength;
+        BytecodeArray code =
+            new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength),
+                              pool);
+        if (observer != null) {
+            code.forEach(new CodeObserver(code.getBytes(), observer));
+        }
+
+        // u2 exception_table_length
+        int exceptionTableLength = bytes.getUnsignedShort(offset);
+        ByteCatchList catches = (exceptionTableLength == 0) ?
+            ByteCatchList.EMPTY :
+            new ByteCatchList(exceptionTableLength);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "exception_table_length: " +
+                            Hex.u2(exceptionTableLength));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length < (exceptionTableLength * 8 + 2)) {
+            return throwTruncated();
+        }
+
+        for (int i = 0; i < exceptionTableLength; i++) {
+            if (observer != null) {
+                observer.changeIndent(1);
+            }
+
+            int startPc = bytes.getUnsignedShort(offset);
+            int endPc = bytes.getUnsignedShort(offset + 2);
+            int handlerPc = bytes.getUnsignedShort(offset + 4);
+            int catchTypeIdx = bytes.getUnsignedShort(offset + 6);
+            CstType catchType = (CstType) pool.get0Ok(catchTypeIdx);
+            catches.set(i, startPc, endPc, handlerPc, catchType);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 8,
+                                Hex.u2(startPc) + ".." + Hex.u2(endPc) +
+                                " -> " + Hex.u2(handlerPc) + " " +
+                                ((catchType == null) ? "<any>" :
+                                 catchType.toHuman()));
+            }
+            offset += 8;
+            length -= 8;
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        catches.setImmutable();
+
+        AttributeListParser parser =
+            new AttributeListParser(cf, CTX_CODE, offset, this);
+        parser.setObserver(observer);
+
+        StdAttributeList attributes = parser.getList();
+        attributes.setImmutable();
+
+        int attributeByteCount = parser.getEndOffset() - offset;
+        if (attributeByteCount != length) {
+            return throwBadLength(attributeByteCount + (offset - origOffset));
+        }
+
+        return new AttCode(maxStack, maxLocals, code, catches, attributes);
+    }
+
+    /**
+     * Parses a <code>ConstantValue</code> attribute.
+     */
+    private Attribute constantValue(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            return throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        TypedConstant cst = (TypedConstant) pool.get(idx);
+        Attribute result = new AttConstantValue(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "value: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a <code>Deprecated</code> attribute.
+     */
+    private Attribute deprecated(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 0) {
+            return throwBadLength(0);
+        }
+
+        return new AttDeprecated();
+    }
+
+    /**
+     * Parses an <code>EnclosingMethod</code> attribute.
+     */
+    private Attribute enclosingMethod(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length != 4) {
+            throwBadLength(4);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+
+        int idx = bytes.getUnsignedShort(offset);
+        CstType type = (CstType) pool.get(idx);
+
+        idx = bytes.getUnsignedShort(offset + 2);
+        CstNat method = (CstNat) pool.get0Ok(idx);
+
+        Attribute result = new AttEnclosingMethod(type, method);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "class: " + type);
+            observer.parsed(bytes, offset + 2, 2, "method: " + 
+                            DirectClassFile.stringOrNone(method));
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses an <code>Exceptions</code> attribute.
+     */
+    private Attribute exceptions(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset); // number_of_exceptions
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "number_of_exceptions: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 2)) {
+            throwBadLength((count * 2) + 2);
+        }
+
+        TypeList list = cf.makeTypeList(offset, count);
+        return new AttExceptions(list);
+    }
+
+    /**
+     * Parses an <code>InnerClasses</code> attribute.
+     */
+    private Attribute innerClasses(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int count = bytes.getUnsignedShort(offset); // number_of_classes
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "number_of_classes: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 8)) {
+            throwBadLength((count * 8) + 2);
+        }
+
+        InnerClassList list = new InnerClassList(count);
+
+        for (int i = 0; i < count; i++) {
+            int innerClassIdx = bytes.getUnsignedShort(offset);
+            int outerClassIdx = bytes.getUnsignedShort(offset + 2);
+            int nameIdx = bytes.getUnsignedShort(offset + 4);
+            int accessFlags = bytes.getUnsignedShort(offset + 6);
+            CstType innerClass = (CstType) pool.get(innerClassIdx);
+            CstType outerClass = (CstType) pool.get0Ok(outerClassIdx);
+            CstUtf8 name = (CstUtf8) pool.get0Ok(nameIdx);
+            list.set(i, innerClass, outerClass, name, accessFlags);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 2,
+                                "inner_class: " + 
+                                DirectClassFile.stringOrNone(innerClass));
+                observer.parsed(bytes, offset + 2, 2,
+                                "  outer_class: " + 
+                                DirectClassFile.stringOrNone(outerClass));
+                observer.parsed(bytes, offset + 4, 2,
+                                "  name: " + 
+                                DirectClassFile.stringOrNone(name));
+                observer.parsed(bytes, offset + 6, 2,
+                                "  access_flags: " +
+                                AccessFlags.innerClassString(accessFlags));
+            }
+            offset += 8;
+        }
+
+        list.setImmutable();
+        return new AttInnerClasses(list);
+    }
+
+    /**
+     * Parses a <code>LineNumberTable</code> attribute.
+     */
+    private Attribute lineNumberTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset); // line_number_table_length
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "line_number_table_length: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 4)) {
+            throwBadLength((count * 4) + 2);
+        }
+
+        LineNumberList list = new LineNumberList(count);
+
+        for (int i = 0; i < count; i++) {
+            int startPc = bytes.getUnsignedShort(offset);
+            int lineNumber = bytes.getUnsignedShort(offset + 2);
+            list.set(i, startPc, lineNumber);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 4,
+                                Hex.u2(startPc) + " " + lineNumber);
+            }
+            offset += 4;
+        }
+
+        list.setImmutable();
+        return new AttLineNumberTable(list);
+    }
+
+    /**
+     * Parses a <code>LocalVariableTable</code> attribute.
+     */
+    private Attribute localVariableTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                    "local_variable_table_length: " + Hex.u2(count));
+        }
+
+        LocalVariableList list = parseLocalVariables(
+                bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+                observer, count, false);
+        return new AttLocalVariableTable(list);
+    }
+
+    /**
+     * Parses a <code>LocalVariableTypeTable</code> attribute.
+     */
+    private Attribute localVariableTypeTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                    "local_variable_type_table_length: " + Hex.u2(count));
+        }
+
+        LocalVariableList list = parseLocalVariables(
+                bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+                observer, count, true);
+        return new AttLocalVariableTypeTable(list);
+    }
+
+    /**
+     * Parse the table part of either a <code>LocalVariableTable</code>
+     * or a <code>LocalVariableTypeTable</code>.
+     * 
+     * @param bytes non-null; bytes to parse, which should <i>only</i>
+     * contain the table data (no header)
+     * @param pool non-null; constant pool to use
+     * @param count &gt;= 0; the number of entries
+     * @param typeTable <code>true</code> iff this is for a type table
+     * @return non-null; the constructed list
+     */
+    private LocalVariableList parseLocalVariables(ByteArray bytes,
+            ConstantPool pool, ParseObserver observer, int count,
+            boolean typeTable) {
+        if (bytes.size() != (count * 10)) {
+            // "+ 2" is for the count.
+            throwBadLength((count * 10) + 2);
+        }
+
+        ByteArray.MyDataInputStream in = bytes.makeDataInputStream();
+        LocalVariableList list = new LocalVariableList(count);
+
+        try {
+            for (int i = 0; i < count; i++) {
+                int startPc = in.readUnsignedShort();
+                int length = in.readUnsignedShort();
+                int nameIdx = in.readUnsignedShort();
+                int typeIdx = in.readUnsignedShort();
+                int index = in.readUnsignedShort();
+                CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+                CstUtf8 type = (CstUtf8) pool.get(typeIdx);
+                CstUtf8 descriptor = null;
+                CstUtf8 signature = null;
+                
+                if (typeTable) {
+                    signature = type;
+                } else {
+                    descriptor = type;
+                }
+                
+                list.set(i, startPc, length, name,
+                        descriptor, signature, index);
+
+                if (observer != null) {
+                    observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) +
+                            ".." + Hex.u2(startPc + length) + " " +
+                            Hex.u2(index) + " " + name.toHuman() + " " +
+                            type.toHuman());
+                }
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        list.setImmutable();
+        return list;
+    }
+
+    /**
+     * Parses a <code>RuntimeInvisibleAnnotations</code> attribute.
+     */
+    private Attribute runtimeInvisibleAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Annotations annotations = 
+            ap.parseAnnotationAttribute(AnnotationVisibility.BUILD);
+
+        return new AttRuntimeInvisibleAnnotations(annotations, length);
+    }
+
+    /**
+     * Parses a <code>RuntimeVisibleAnnotations</code> attribute.
+     */
+    private Attribute runtimeVisibleAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Annotations annotations = 
+            ap.parseAnnotationAttribute(AnnotationVisibility.RUNTIME);
+
+        return new AttRuntimeVisibleAnnotations(annotations, length);
+    }
+
+    /**
+     * Parses a <code>RuntimeInvisibleParameterAnnotations</code> attribute.
+     */
+    private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        AnnotationsList list =
+            ap.parseParameterAttribute(AnnotationVisibility.BUILD);
+
+        return new AttRuntimeInvisibleParameterAnnotations(list, length);
+    }
+
+    /**
+     * Parses a <code>RuntimeVisibleParameterAnnotations</code> attribute.
+     */
+    private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        AnnotationsList list =
+            ap.parseParameterAttribute(AnnotationVisibility.RUNTIME);
+
+        return new AttRuntimeVisibleParameterAnnotations(list, length);
+    }
+
+    /**
+     * Parses a <code>Signature</code> attribute.
+     */
+    private Attribute signature(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        CstUtf8 cst = (CstUtf8) pool.get(idx);
+        Attribute result = new AttSignature(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "signature: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a <code>SourceFile</code> attribute.
+     */
+    private Attribute sourceFile(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        CstUtf8 cst = (CstUtf8) pool.get(idx);
+        Attribute result = new AttSourceFile(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "source: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a <code>Synthetic</code> attribute.
+     */
+    private Attribute synthetic(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 0) {
+            return throwBadLength(0);
+        }
+
+        return new AttSynthetic();
+    }
+
+    /**
+     * Throws the right exception when a known attribute has a way too short
+     * length.
+     * 
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwSeverelyTruncated() {
+        throw new ParseException("severely truncated attribute");
+    }
+
+    /**
+     * Throws the right exception when a known attribute has a too short
+     * length.
+     * 
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwTruncated() {
+        throw new ParseException("truncated attribute");
+    }
+
+    /**
+     * Throws the right exception when an attribute has an unexpected length
+     * (given its contents).
+     * 
+     * @param expected expected length
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwBadLength(int expected) {
+        throw new ParseException("bad attribute length; expected length " +
+                                 Hex.u4(expected));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/package.html b/dx/src/com/android/dx/cf/direct/package.html
new file mode 100644
index 0000000..2a46198
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/package.html
@@ -0,0 +1,12 @@
+<body>
+<p>Implementation of <code>cf.iface.*</code> based on a direct representation
+of class files as <code>byte[]</code>s.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.attrib</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/iface/Attribute.java b/dx/src/com/android/dx/cf/iface/Attribute.java
new file mode 100644
index 0000000..f28f51e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Attribute.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface representing attributes of class files (directly or indirectly).
+ */
+public interface Attribute {
+    /**
+     * Get the name of the attribute.
+     *
+     * @return non-null; the name
+     */
+    public String getName();
+
+    /**
+     * Get the total length of the attribute in bytes, including the
+     * header. Since the header is always six bytes, the result of
+     * this method is always at least <code>6</code>.
+     *
+     * @return &gt;= 6; the total length, in bytes
+     */
+    public int byteLength();
+}
diff --git a/dx/src/com/android/dx/cf/iface/AttributeList.java b/dx/src/com/android/dx/cf/iface/AttributeList.java
new file mode 100644
index 0000000..a72965a
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/AttributeList.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of attributes.
+ */
+public interface AttributeList {
+    /**
+     * Get whether this instance is mutable. Note that the
+     * <code>AttributeList</code> interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return <code>true</code> iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of attributes in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the <code>n</code>th attribute.
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; which attribute
+     * @return non-null; the attribute in question
+     */
+    public Attribute get(int n);
+
+    /**
+     * Get the total length of this list in bytes, when part of a
+     * class file. The returned value includes the two bytes for the
+     * <code>attributes_count</code> length indicator.
+     *
+     * @return &gt;= 2; the total length, in bytes
+     */
+    public int byteLength();
+
+    /**
+     * Get the first attribute in the list with the given name, if any.
+     *
+     * @param name non-null; attribute name
+     * @return null-ok; first attribute in the list with the given name,
+     * or <code>null</code> if there is none
+     */
+    public Attribute findFirst(String name);
+
+    /**
+     * Get the next attribute in the list after the given one, with the same
+     * name, if any.
+     *
+     * @param attrib non-null; attribute to start looking after
+     * @return null-ok; next attribute after <code>attrib</code> with the
+     * same name as <code>attrib</code>
+     */
+    public Attribute findNext(Attribute attrib);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ClassFile.java b/dx/src/com/android/dx/cf/iface/ClassFile.java
new file mode 100644
index 0000000..36a5f74
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ClassFile.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Interface for things which purport to be class files or reasonable
+ * facsimiles thereof.
+ *
+ * <p><b>Note:</b> The fields referred to in this documentation are of the
+ * <code>ClassFile</code> structure defined in vmspec-2 sec4.1.
+ */
+public interface ClassFile {
+    /**
+     * Gets the field <code>magic</code>.
+     *
+     * @return the value in question
+     */
+    public int getMagic();
+
+    /**
+     * Gets the field <code>minor_version</code>.
+     *
+     * @return the value in question
+     */
+    public int getMinorVersion();
+
+    /**
+     * Gets the field <code>major_version</code>.
+     *
+     * @return the value in question
+     */
+    public int getMajorVersion();
+
+    /**
+     * Gets the field <code>access_flags</code>.
+     *
+     * @return the value in question
+     */
+    public int getAccessFlags();
+
+    /**
+     * Gets the field <code>this_class</code>, interpreted as a type constant.
+     *
+     * @return non-null; the value in question
+     */
+    public CstType getThisClass();
+
+    /**
+     * Gets the field <code>super_class</code>, interpreted as a type constant
+     * if non-zero.
+     *
+     * @return null-ok; the value in question
+     */
+    public CstType getSuperclass();
+
+    /**
+     * Gets the field <code>constant_pool</code> (along with
+     * <code>constant_pool_count</code>).
+     *
+     * @return non-null; the constant pool
+     */
+    public ConstantPool getConstantPool();
+
+    /**
+     * Gets the field <code>interfaces<code> (along with 
+     * interfaces_count</code>).
+     *
+     * @return non-null; the list of interfaces
+     */
+    public TypeList getInterfaces();
+
+    /**
+     * Gets the field <code>fields</code> (along with
+     * <code>fields_count</code>).
+     *
+     * @return non-null; the list of fields
+     */
+    public FieldList getFields();
+
+    /**
+     * Gets the field <code>methods</code> (along with
+     * <code>methods_count</code>).
+     *
+     * @return non-null; the list of fields
+     */
+    public MethodList getMethods();
+
+    /**
+     * Gets the field <code>attributes</code> (along with
+     * <code>attributes_count</code>).
+     *
+     * @return non-null; the list of attributes
+     */
+    public AttributeList getAttributes();
+
+    /**
+     * Gets the name out of the <code>SourceFile</code> attribute of this
+     * file, if any. This is a convenient shorthand for scrounging around
+     * the class's attributes.
+     *
+     * @return non-null; the constant pool
+     */
+    public CstUtf8 getSourceFile();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Field.java b/dx/src/com/android/dx/cf/iface/Field.java
new file mode 100644
index 0000000..d1694fc
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Field.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Interface representing fields of class files.
+ */
+public interface Field
+        extends Member {
+    /**
+     * Get the constant value for this field, if any. This only returns
+     * non-<code>null</code> for a <code>static final</code> field which
+     * includes a <code>ConstantValue</code> attribute.
+     *
+     * @return null-ok; the constant value, or <code>null</code> if this
+     * field isn't a constant
+     */
+    public TypedConstant getConstantValue();
+}
diff --git a/dx/src/com/android/dx/cf/iface/FieldList.java b/dx/src/com/android/dx/cf/iface/FieldList.java
new file mode 100644
index 0000000..80a794f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/FieldList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of fields.
+ */
+public interface FieldList
+{
+    /**
+     * Get whether this instance is mutable. Note that the
+     * <code>FieldList</code> interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return <code>true</code> iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of fields in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the <code>n</code>th field.
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; which field
+     * @return non-null; the field in question
+     */
+    public Field get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/Member.java b/dx/src/com/android/dx/cf/iface/Member.java
new file mode 100644
index 0000000..b305e09
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Member.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Interface representing members of class files (that is, fields and methods).
+ */
+public interface Member {
+    /**
+     * Get the defining class.
+     *
+     * @return non-null; the defining class
+     */
+    public CstType getDefiningClass();
+
+    /**
+     * Get the field <code>access_flags</code>.
+     *
+     * @return the access flags
+     */
+    public int getAccessFlags();
+
+    /**
+     * Get the field <code>name_index</code> of the member. This is
+     * just a convenient shorthand for <code>getNat().getName()</code>.
+     *
+     * @return non-null; the name
+     */
+    public CstUtf8 getName();
+
+    /**
+     * Get the field <code>descriptor_index</code> of the member. This is
+     * just a convenient shorthand for <code>getNat().getDescriptor()</code>.
+     *
+     * @return non-null; the descriptor
+     */
+    public CstUtf8 getDescriptor();
+
+    /**
+     * Get the name and type associated with this member. This is a
+     * combination of the fields <code>name_index</code> and
+     * <code>descriptor_index</code> in the original classfile, interpreted
+     * via the constant pool.
+     *
+     * @return non-null; the name and type
+     */
+    public CstNat getNat();
+
+    /**
+     * Get the field <code>attributes</code> (along with
+     * <code>attributes_count</code>).
+     *
+     * @return non-null; the constant pool
+     */
+    public AttributeList getAttributes();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Method.java b/dx/src/com/android/dx/cf/iface/Method.java
new file mode 100644
index 0000000..424400a
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Method.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Interface representing methods of class files.
+ */
+public interface Method
+    extends Member
+{
+    /**
+     * Get the <i>effective</i> method descriptor, which includes, if
+     * necessary, a first <code>this</code> parameter.
+     *
+     * @return non-null; the effective method descriptor
+     */
+    public Prototype getEffectiveDescriptor();
+}
diff --git a/dx/src/com/android/dx/cf/iface/MethodList.java b/dx/src/com/android/dx/cf/iface/MethodList.java
new file mode 100644
index 0000000..a7a395c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/MethodList.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of methods.
+ */
+public interface MethodList {
+    /**
+     * Get whether this instance is mutable. Note that the
+     * <code>MethodList</code> interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return <code>true</code> iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of methods in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the <code>n</code>th method.
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; which method
+     * @return non-null; the method in question
+     */
+    public Method get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseException.java b/dx/src/com/android/dx/cf/iface/ParseException.java
new file mode 100644
index 0000000..18a9d0e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from parsing.
+ */
+public class ParseException
+        extends ExceptionWithContext {
+    public ParseException(String message) {
+        super(message);
+    }
+
+    public ParseException(Throwable cause) {
+        super(cause);
+    }
+
+    public ParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseObserver.java b/dx/src/com/android/dx/cf/iface/ParseObserver.java
new file mode 100644
index 0000000..2ad3493
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseObserver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ByteArray;
+
+/**
+ * Observer of parsing in action. This is used to supply feedback from
+ * the various things that parse particularly to the dumping utilities.
+ */
+public interface ParseObserver {
+    /**
+     * Indicate that the level of indentation for a dump should increase
+     * or decrease (positive or negative argument, respectively).
+     *
+     * @param indentDelta the amount to change indentation
+     */
+    public void changeIndent(int indentDelta);
+
+    /**
+     * Indicate that a particular member is now being parsed.
+     *
+     * @param bytes non-null; the source that is being parsed
+     * @param offset offset into <code>bytes</code> for the start of the
+     * member
+     * @param name non-null; name of the member
+     * @param descriptor non-null; descriptor of the member
+     */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor);
+
+    /**
+     * Indicate that a particular member is no longer being parsed.
+     *
+     * @param bytes non-null; the source that was parsed
+     * @param offset offset into <code>bytes</code> for the end of the
+     * member
+     * @param name non-null; name of the member
+     * @param descriptor non-null; descriptor of the member
+     * @param member non-null; the actual member that was parsed
+     */
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member);
+
+    /**
+     * Indicate that some parsing happened.
+     *
+     * @param bytes non-null; the source that was parsed
+     * @param offset offset into <code>bytes</code> for what was parsed
+     * @param len number of bytes parsed
+     * @param human non-null; human form for what was parsed
+     */
+    public void parsed(ByteArray bytes, int offset, int len, String human);
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdAttributeList.java b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
new file mode 100644
index 0000000..c29bb08
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link AttributeList}, which directly stores
+ * an array of {@link Attribute} objects and can be made immutable.
+ */
+public final class StdAttributeList extends FixedSizeList
+        implements AttributeList {
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public StdAttributeList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Attribute get(int n) {
+        return (Attribute) get0(n);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        int sz = size();
+        int result = 2; // u2 attributes_count
+
+        for (int i = 0; i < sz; i++) {
+            result += get(i).byteLength();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Attribute findFirst(String name) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Attribute att = get(i);
+            if (att.getName().equals(name)) {
+                return att;
+            }
+        }
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public Attribute findNext(Attribute attrib) {
+        int sz = size();
+        int at;
+
+        outer: {
+            for (at = 0; at < sz; at++) {
+                Attribute att = get(at);
+                if (att == attrib) {
+                    break outer;
+                }
+            }
+
+            return null;
+        }
+
+        String name = attrib.getName();
+
+        for (at++; at < sz; at++) {
+            Attribute att = get(at);
+            if (att.getName().equals(name)) {
+                return att;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the attribute at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which attribute
+     * @param attribute null-ok; the attribute object
+     */
+    public void set(int n, Attribute attribute) {
+        set0(n, attribute);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdField.java b/dx/src/com/android/dx/cf/iface/StdField.java
new file mode 100644
index 0000000..3551aee
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdField.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Standard implementation of {@link Field}, which directly stores
+ * all the associated data.
+ */
+public final class StdField extends StdMember implements Field {
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the defining class
+     * @param accessFlags access flags
+     * @param nat non-null; member name and type (descriptor)
+     * @param attributes non-null; list of associated attributes
+     */
+    public StdField(CstType definingClass, int accessFlags, CstNat nat,
+                    AttributeList attributes) {
+        super(definingClass, accessFlags, nat, attributes);
+    }
+
+    /** {@inheritDoc} */
+    public TypedConstant getConstantValue() {
+        AttributeList attribs = getAttributes();
+        AttConstantValue cval = (AttConstantValue)
+            attribs.findFirst(AttConstantValue.ATTRIBUTE_NAME);
+
+        if (cval == null) {
+            return null;
+        }
+
+        return cval.getConstantValue();
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdFieldList.java b/dx/src/com/android/dx/cf/iface/StdFieldList.java
new file mode 100644
index 0000000..0f8654b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdFieldList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link FieldList}, which directly stores
+ * an array of {@link Field} objects and can be made immutable.
+ */
+public final class StdFieldList extends FixedSizeList implements FieldList {
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public StdFieldList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Field get(int n) {
+        return (Field) get0(n);
+    }
+
+    /**
+     * Sets the field at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which field
+     * @param field null-ok; the field object
+     */
+    public void set(int n, Field field) {
+        set0(n, field);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMember.java b/dx/src/com/android/dx/cf/iface/StdMember.java
new file mode 100644
index 0000000..eaf949e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMember.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Standard implementation of {@link Member}, which directly stores
+ * all the associated data.
+ */
+public abstract class StdMember implements Member {
+    /** non-null; the defining class */
+    private final CstType definingClass;
+
+    /** access flags */
+    private final int accessFlags;
+
+    /** non-null; member name and type */
+    private final CstNat nat;
+
+    /** non-null; list of associated attributes */
+    private final AttributeList attributes;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the defining class
+     * @param accessFlags access flags
+     * @param nat non-null; member name and type (descriptor)
+     * @param attributes non-null; list of associated attributes
+     */
+    public StdMember(CstType definingClass, int accessFlags, CstNat nat,
+                     AttributeList attributes) {
+        if (definingClass == null) {
+            throw new NullPointerException("definingClass == null");
+        }
+
+        if (nat == null) {
+            throw new NullPointerException("nat == null");
+        }
+
+        if (attributes == null) {
+            throw new NullPointerException("attributes == null");
+        }
+
+        this.definingClass = definingClass;
+        this.accessFlags = accessFlags;
+        this.nat = nat;
+        this.attributes = attributes;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(nat.toHuman());
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    public final CstType getDefiningClass() {
+        return definingClass;
+    }
+
+    /** {@inheritDoc} */
+    public final int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /** {@inheritDoc} */
+    public final CstNat getNat() {
+        return nat;
+    }
+
+    /** {@inheritDoc} */
+    public final CstUtf8 getName() {
+        return nat.getName();
+    }
+
+    /** {@inheritDoc} */
+    public final CstUtf8 getDescriptor() {
+        return nat.getDescriptor();
+    }
+
+    /** {@inheritDoc} */
+    public final AttributeList getAttributes() {
+        return attributes;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethod.java b/dx/src/com/android/dx/cf/iface/StdMethod.java
new file mode 100644
index 0000000..a4acbaa
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethod.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Standard implementation of {@link Method}, which directly stores
+ * all the associated data.
+ */
+public final class StdMethod extends StdMember implements Method {
+    /** non-null; the effective method descriptor */
+    private final Prototype effectiveDescriptor;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the defining class
+     * @param accessFlags access flags
+     * @param nat non-null; member name and type (descriptor)
+     * @param attributes non-null; list of associated attributes
+     */
+    public StdMethod(CstType definingClass, int accessFlags, CstNat nat,
+            AttributeList attributes) {
+        super(definingClass, accessFlags, nat, attributes);
+
+        String descStr = getDescriptor().getString();
+        effectiveDescriptor =
+            Prototype.intern(descStr, definingClass.getClassType(),
+                                    AccessFlags.isStatic(accessFlags),
+                                    nat.isInstanceInit());
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getEffectiveDescriptor() {
+        return effectiveDescriptor;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethodList.java b/dx/src/com/android/dx/cf/iface/StdMethodList.java
new file mode 100644
index 0000000..ef0ff31
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethodList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link MethodList}, which directly stores
+ * an array of {@link Method} objects and can be made immutable.
+ */
+public final class StdMethodList extends FixedSizeList implements MethodList {
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public StdMethodList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Method get(int n) {
+        return (Method) get0(n);
+    }
+
+    /**
+     * Sets the method at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which method
+     * @param method null-ok; the method object
+     */
+    public void set(int n, Method method) {
+        set0(n, method);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/package.html b/dx/src/com/android/dx/cf/iface/package.html
new file mode 100644
index 0000000..c734552
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Interfaces and base classes for dealing with class files. This package
+doesn't have any parsing but does have basic container implementations.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/command/DxConsole.java b/dx/src/com/android/dx/command/DxConsole.java
new file mode 100644
index 0000000..982e659
--- /dev/null
+++ b/dx/src/com/android/dx/command/DxConsole.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+import java.io.PrintStream;
+
+/**
+ * Provides standard and error PrintStream object to output information.<br>
+ * By default the PrintStream objects link to <code>System.out</code> and
+ * <code>System.err</code> but they can be changed to link to other
+ * PrintStream.
+ */
+public class DxConsole {
+    /**
+     * Standard output stream. Links to <code>System.out</code> by default.
+     */
+    public static PrintStream out = System.out;
+
+    /**
+     * Error output stream. Links to <code>System.err</code> by default.
+     */
+    public static PrintStream err = System.err;
+}
diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java
new file mode 100644
index 0000000..17f5db3
--- /dev/null
+++ b/dx/src/com/android/dx/command/Main.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+import com.android.dx.Version;
+
+import junit.textui.TestRunner;
+
+/**
+ * Main class for dx. It recognizes enough options to be able to dispatch
+ * to the right "actual" main.
+ */
+public class Main {
+    private static String USAGE_MESSAGE =
+        "usage:\n" +
+        "  dx --dex [--debug] [--verbose] [--positions=<style>] " +
+        "[--no-locals]\n" +
+        "  [--no-optimize] [--statistics] [--[no-]optimize-list=<file>] " +
+        "[--no-strict]\n" +
+        "  [--keep-classes] [--output=<file>] [--dump-to=<file>] " +
+        "[--dump-width=<n>]\n" +
+        "  [--dump-method=<name>[*]] [--verbose-dump] [--no-files] " +
+        "[--core-library]\n" +
+        "  [<file>.class | <file>.{zip,jar,apk} | <directory>] ...\n" +
+        "    Convert a set of classfiles into a dex file, optionally " +
+        "embedded in a\n" +
+        "    jar/zip. Output name must end with one of: .dex .jar " +
+        ".zip .apk. Positions\n" +
+        "    options: none, important, lines.\n" +
+        "  dx --annotool --annotation=<class> [--element=<element types>]\n" +
+        "  [--print=<print types>]\n" +
+        "  dx --dump [--debug] [--strict] [--bytes] [--basic-blocks | " +
+        "--rop-blocks]\n" +
+        "  [--width=<n>] [<file>.class | <file>.txt] ...\n" +
+        "    Dump classfiles in a human-oriented format.\n" +
+        "  dx --junit [-wait] <TestClass>\n" +
+        "    Run the indicated unit test.\n" + 
+        "  dx -J<option> ... <arguments, in one of the above " +
+        "forms>\n" +
+        "    Pass VM-specific options to the virtual machine that " +
+        "runs dx.\n" +
+        "  dx --version\n" +
+        "    Print the version of this tool (" + Version.VERSION +
+        ").\n" +
+        "  dx --help\n" +
+        "    Print this message.";
+        
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run!
+     */
+    public static void main(String[] args) {
+        boolean gotCmd = false;
+        boolean showUsage = false;
+
+        try {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
+                if (arg.equals("--") || !arg.startsWith("--")) {
+                    gotCmd = false;
+                    showUsage = true;
+                    break;
+                }
+
+                gotCmd = true;
+                if (arg.equals("--dex")) {
+                    com.android.dx.command.dexer.Main.main(without(args, i));
+                    break;
+                } else if (arg.equals("--dump")) {
+                    com.android.dx.command.dump.Main.main(without(args, i));
+                    break;
+                } else if (arg.equals("--annotool")) {
+                    com.android.dx.command.annotool.Main.main(
+                            without(args, i));
+                    break;
+                } else if (arg.equals("--junit")) {
+                    TestRunner.main(without(args, i));
+                    break;
+                } else if (arg.equals("--version")) {
+                    version();
+                    break;
+                } else if (arg.equals("--help")) {
+                    showUsage = true;
+                    break;
+                } else {
+                    gotCmd = false;
+                }
+            }
+        } catch (UsageException ex) {
+            showUsage = true;
+        } catch (RuntimeException ex) {
+            System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+            ex.printStackTrace();
+            System.exit(2);
+        } catch (Throwable ex) {
+            System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
+            ex.printStackTrace();
+            if ((ex instanceof NoClassDefFoundError) 
+                    || (ex instanceof NoSuchMethodError)) {
+                System.err.println(
+                        "Note: You may be using an incompatible " +
+                        "virtual machine or class library.\n" +
+                        "(This program is known to be incompatible " +
+                        "with recent releases of GCJ.)");
+            }
+            System.exit(3);
+        }            
+
+        if (!gotCmd) {
+            System.err.println("error: no command specified");
+            showUsage = true;
+        }
+
+        if (showUsage) {
+            usage();
+            System.exit(1);
+        }
+    }
+
+    /**
+     * Prints the version message.
+     */
+    private static void version() {
+        System.err.println("dx version " + Version.VERSION);
+        System.exit(0);
+    }
+
+    /**
+     * Prints the usage message.
+     */
+    private static void usage() {
+        System.err.println(USAGE_MESSAGE);
+    }
+
+    /**
+     * Returns a copy of the given args array, but without the indicated
+     * element.
+     *
+     * @param orig non-null; original array
+     * @param n which element to omit
+     * @return non-null; new array
+     */
+    private static String[] without(String[] orig, int n) {
+        int len = orig.length - 1;
+        String[] newa = new String[len];
+        System.arraycopy(orig, 0, newa, 0, n);
+        System.arraycopy(orig, n + 1, newa, n, len - n);
+        return newa;
+    }
+}
diff --git a/dx/src/com/android/dx/command/UsageException.java b/dx/src/com/android/dx/command/UsageException.java
new file mode 100644
index 0000000..6809bf4
--- /dev/null
+++ b/dx/src/com/android/dx/command/UsageException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+/**
+ * Simple exception class used to communicate that the command-line tool
+ * should print the usage message.
+ */
+public class UsageException extends RuntimeException {
+    // This space intentionally left blank.
+}
diff --git a/dx/src/com/android/dx/command/annotool/AnnotationLister.java b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
new file mode 100644
index 0000000..78f5d64
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.HashSet;
+
+/**
+ * Greps annotations on a set of class files and prints matching elements
+ * to stdout. What counts as a match and what should be printed is controlled
+ * by the <code>Main.Arguments</code> instance.
+ */
+class AnnotationLister {
+
+    /**
+     * The string name of the pseudo-class that
+     * contains package-wide annotations
+     */
+    private static final String PACKAGE_INFO = "package-info";
+
+    /** current match configuration */
+    private final Main.Arguments args;
+
+    /** Set of classes whose inner classes should be considered matched */
+    HashSet<String> matchInnerClassesOf = new HashSet<String>();
+
+    /** set of packages whose classes should be considered matched */
+    HashSet<String> matchPackages = new HashSet<String>();
+
+    AnnotationLister (Main.Arguments args) {
+        this.args = args;
+    }
+
+    /** Processes based on configuration specified in constructor. */
+    void process() {
+        for (String path: args.files) {
+            ClassPathOpener opener;
+
+            opener = new ClassPathOpener(path, true,
+                    new ClassPathOpener.Consumer() {
+                public boolean processFileBytes(String name, byte[] bytes) {
+                    if (!name.endsWith(".class")) {
+                        return true;
+                    }
+
+                    ByteArray ba = new ByteArray(bytes);
+                    DirectClassFile cf
+                        = new DirectClassFile(ba, name, true);
+
+                    cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+                    AttributeList attributes = cf.getAttributes();
+                    Attribute att;
+
+                    String cfClassName
+                            = cf.getThisClass().getClassType().getClassName();
+
+                    if (cfClassName.endsWith(PACKAGE_INFO)) {
+                        att = attributes.findFirst(
+                                AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitPackageAnnotation(cf, ann);
+                        }
+
+                        att = attributes.findFirst(
+                                AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitPackageAnnotation(cf, ann);
+                        }
+                    } else if (isMatchingInnerClass(cfClassName)
+                            || isMatchingPackage(cfClassName)) {
+                        printMatch(cf);
+                    } else {
+                        att = attributes.findFirst(
+                                AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitClassAnnotation(cf, ann);
+                        }
+
+                        att = attributes.findFirst(
+                                AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitClassAnnotation(cf, ann);
+                        }
+                    }
+
+                    return true;
+                }
+
+                public void onException(Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+
+                public void onProcessArchiveStart(File file) {
+
+                }
+
+            });
+
+            opener.process();
+        }
+    }
+
+    /**
+     * Inspects a class annotation.
+     *
+     * @param cf non-null; class file
+     * @param ann non-null; annotation
+     */
+    private void visitClassAnnotation(DirectClassFile cf,
+            BaseAnnotations ann) {
+
+        if (!args.eTypes.contains(ElementType.TYPE)) {
+            return;
+        }
+
+        for (Annotation anAnn: ann.getAnnotations().getAnnotations()) {
+            String annClassName
+                    = anAnn.getType().getClassType().getClassName();
+            if (args.aclass.equals(annClassName)) {
+                printMatch(cf);
+            }
+        }
+    }
+
+    /**
+     * Inspects a package annotation
+     *
+     * @param cf non-null; class file of "package-info" pseudo-class
+     * @param ann non-null; annotation
+     */
+    private void visitPackageAnnotation(
+            DirectClassFile cf, BaseAnnotations ann) {
+
+        if (!args.eTypes.contains(ElementType.PACKAGE)) {
+            return;
+        }
+
+        String packageName = cf.getThisClass().getClassType().getClassName();
+
+        int slashIndex = packageName.lastIndexOf('/');
+
+        if (slashIndex == -1) {
+            packageName = "";
+        } else {
+            packageName
+                    = packageName.substring(0, slashIndex);
+        }
+
+
+        for (Annotation anAnn: ann.getAnnotations().getAnnotations()) {
+            String annClassName
+                    = anAnn.getType().getClassType().getClassName();
+            if (args.aclass.equals(annClassName)) {
+                printMatchPackage(packageName);
+            }
+        }
+    }
+
+
+    /**
+     * Prints, or schedules for printing, elements related to a
+     * matching package.
+     *
+     * @param packageName non-null; name of package
+     */
+    private void printMatchPackage(String packageName) {
+        for(Main.PrintType pt: args.printTypes) {
+            switch (pt) {
+                case CLASS:
+                case INNERCLASS:
+                case METHOD:
+                    matchPackages.add(packageName);                    
+                    break;
+                case PACKAGE:
+                    System.out.println(packageName.replace('/','.'));
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Prints, or schedules for printing, elements related to a matching
+     * class.
+     *
+     * @param cf non-null; matching class
+     */
+    private void printMatch(DirectClassFile cf) {
+        for(Main.PrintType pt: args.printTypes) {
+            switch (pt) {
+                case CLASS:
+                    String classname;
+                    classname = cf.getThisClass().getClassType().getClassName();
+                    classname = classname.replace('/','.');
+                    System.out.println(classname);
+                    break;
+                case INNERCLASS:
+                    matchInnerClassesOf.add(
+                            cf.getThisClass().getClassType().getClassName());
+                    break;
+                case METHOD:
+                    //TODO
+                    break;
+                case PACKAGE:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Checks to see if a specified class name should be considered a match
+     * due to previous matches.
+     *
+     * @param s non-null; class name
+     * @return true if this class should be considered a match
+     */
+    private boolean isMatchingInnerClass(String s) {
+        int i;
+
+        while (0 < (i = s.lastIndexOf('$'))) {
+            s = s.substring(0, i);
+            if (matchInnerClassesOf.contains(s)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks to see if a specified package should be considered a match due
+     * to previous matches.
+     *
+     * @param s non-null; package name
+     * @return true if this package should be considered a match
+     */
+    private boolean isMatchingPackage(String s) {
+        int slashIndex = s.lastIndexOf('/');
+
+        String packageName;
+        if (slashIndex == -1) {
+            packageName = "";
+        } else {
+            packageName
+                    = s.substring(0, slashIndex);
+        }
+
+        return matchPackages.contains(packageName);
+    }
+}
diff --git a/dx/src/com/android/dx/command/annotool/Main.java b/dx/src/com/android/dx/command/annotool/Main.java
new file mode 100644
index 0000000..4f1d9a4
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/Main.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.EnumSet;
+import java.util.Arrays;
+
+
+public class Main {
+
+    private static class InvalidArgumentException extends Exception {
+        InvalidArgumentException() {
+            super();
+        }
+        
+        InvalidArgumentException(String s) {
+            super(s);
+        }
+    }
+
+    enum PrintType {
+        CLASS,
+        INNERCLASS,
+        METHOD,
+        PACKAGE        
+    }
+
+
+    static class Arguments {
+        /**
+         * from --annotation, dot-seperated classname
+         * of annotation to look for
+         */
+        String aclass;
+
+        /** from --eTypes */
+        EnumSet<ElementType> eTypes = EnumSet.noneOf(ElementType.class);
+
+        /** from --print */
+        EnumSet<PrintType> printTypes = EnumSet.noneOf(PrintType.class);
+
+        /** remaining positional arguments */
+        String[] files;
+
+        Arguments() {
+        }
+
+        void parse (String[] argArray) throws InvalidArgumentException {
+            for (int i = 0; i < argArray.length; i++) {
+                String arg = argArray[i];
+
+                if (arg.startsWith("--annotation=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+                    if (aclass != null) {
+                        throw new InvalidArgumentException(
+                                "--annotation can only be specified once.");
+                    }
+                    aclass = argParam.replace('.','/');
+                } else if (arg.startsWith("--element=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+
+                    try {
+                        for (String p: argParam.split(",")) {
+                            eTypes.add(ElementType.valueOf(p.toUpperCase()));
+                        }
+                    } catch (IllegalArgumentException ex) {
+                        throw new InvalidArgumentException("invalid --element");
+                    }
+                } else if (arg.startsWith("--print=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+
+                    try {
+                        for (String p: argParam.split(",")) {
+                            printTypes.add(PrintType.valueOf(p.toUpperCase()));
+                        }
+                    } catch (IllegalArgumentException ex) {
+                        throw new InvalidArgumentException("invalid --print");
+                    }
+                } else {
+                    files = new String[argArray.length - i];
+                    System.arraycopy(argArray, i, files, 0, files.length);
+                    break;
+                }
+            }
+
+            if (aclass == null) {
+                throw new InvalidArgumentException(
+                        "--annotation must be specified");
+            }
+
+            if (printTypes.isEmpty()) {
+                printTypes.add(PrintType.CLASS);
+            }
+
+            if (eTypes.isEmpty()) {
+                eTypes.add(ElementType.TYPE);
+            }
+
+            EnumSet<ElementType> set = eTypes.clone();
+
+            set.remove(ElementType.TYPE);
+            set.remove(ElementType.PACKAGE);
+            if (!set.isEmpty()) {
+                throw new InvalidArgumentException(
+                        "only --element parameters 'type' and 'package' "
+                                + "supported");
+            }
+        }
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    public static void main(String[] argArray) {
+
+        final Arguments args = new Arguments();
+
+        try {
+            args.parse(argArray);
+        } catch (InvalidArgumentException ex) {
+            System.err.println(ex.getMessage());
+
+            throw new RuntimeException("usage");
+        }
+
+        new AnnotationLister(args).process();
+    }
+}
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
new file mode 100644
index 0000000..19f67b4
--- /dev/null
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dexer;
+
+import com.android.dx.Version;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.command.DxConsole;
+import com.android.dx.command.UsageException;
+import com.android.dx.dex.cf.CfOptions;
+import com.android.dx.dex.cf.CfTranslator;
+import com.android.dx.dex.cf.CodeStatistics;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.DexFile;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstUtf8;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * Main class for the class file translator.
+ */
+public class Main {
+    /**
+     * non-null; name for the <code>.dex</code> file that goes into
+     * <code>.jar</code> files
+     */
+    private static final String DEX_IN_JAR_NAME = "classes.dex";
+
+    /**
+     * non-null; name of the standard manifest file in <code>.jar</code>
+     * files
+     */
+    private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+    /**
+     * non-null; attribute name for the (quasi-standard?)
+     * <code>Created-By</code> attribute
+     */
+    private static final Attributes.Name CREATED_BY =
+        new Attributes.Name("Created-By");
+
+    /**
+     * non-null; list of <code>javax</code> subpackages that are considered
+     * to be "core". <b>Note:</b>: This list must be sorted, since it
+     * is binary-searched.
+     */
+    private static final String[] JAVAX_CORE = {
+        "accessibility", "crypto", "imageio", "management", "naming", "net",
+        "print", "rmi", "security", "sound", "sql", "swing", "transaction",
+        "xml"
+    };
+
+    /** number of warnings during processing */
+    private static int warnings = 0;
+
+    /** number of errors during processing */
+    private static int errors = 0;
+
+    /** non-null; parsed command-line arguments */
+    private static Arguments args;
+
+    /** non-null; output file in-progress */
+    private static DexFile outputDex;
+
+    /**
+     * null-ok; map of resources to include in the output, or
+     * <code>null</code> if resources are being ignored
+     */
+    private static TreeMap<String, byte[]> outputResources;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run and exit if something unexpected happened.
+     * @param argArray the command line arguments
+     */
+    public static void main(String[] argArray) {
+        Arguments arguments = new Arguments();
+        arguments.parse(argArray);
+
+        int result = run(arguments);
+        if (result != 0) {
+            System.exit(result);
+        }
+    }
+
+    /**
+     * Run and return a result code.
+     * @param arguments the data + parameters for the conversion
+     * @return 0 if success > 0 otherwise.
+     */
+    public static int run(Arguments arguments) {
+        // Reset the error/warning count to start fresh.
+        warnings = 0;
+        errors = 0;
+
+        args = arguments;
+        args.makeCfOptions();
+
+        if (!processAllFiles()) {
+            return 1;
+        }
+
+        byte[] outArray = writeDex();
+
+        if (outArray == null) {
+            return 2;
+        }
+
+        if (args.jarOutput) {
+            // Effectively free up the (often massive) DexFile memory.
+            outputDex = null;
+
+            if (!createJar(args.outName, outArray)) {
+                return 3;
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Constructs the output {@link DexFile}, fill it in with all the
+     * specified classes, and populate the resources map if required.
+     *
+     * @return whether processing was successful
+     */
+    private static boolean processAllFiles() {
+        outputDex = new DexFile();
+
+        if (args.jarOutput) {
+            outputResources = new TreeMap<String, byte[]>();
+        }
+
+        if (args.dumpWidth != 0) {
+            outputDex.setDumpWidth(args.dumpWidth);
+        }
+
+        boolean any = false;
+        String[] fileNames = args.fileNames;
+
+        try {
+            for (int i = 0; i < fileNames.length; i++) {
+                any |= processOne(fileNames[i]);
+            }
+        } catch (StopProcessing ex) {
+            /*
+             * Ignore it and just let the warning/error reporting do
+             * their things.
+             */
+        }
+
+        if (warnings != 0) {
+            DxConsole.err.println(warnings + " warning" +
+                               ((warnings == 1) ? "" : "s"));
+        }
+
+        if (errors != 0) {
+            DxConsole.err.println(errors + " error" +
+                    ((errors == 1) ? "" : "s") + "; aborting");
+            return false;
+        }
+
+        if (!(any || args.emptyOk)) {
+            DxConsole.err.println("no classfiles specified");
+            return false;
+        }
+
+        if (args.optimize && args.statistics) {
+            CodeStatistics.dumpStatistics(DxConsole.out);
+        }
+
+        return true;
+    }
+
+    /**
+     * Processes one pathname element.
+     *
+     * @param pathname non-null; the pathname to process. May be the path of
+     * a class file, a jar file, or a directory containing class files.
+     * @return whether any processing actually happened
+     */
+    private static boolean processOne(String pathname) {
+        ClassPathOpener opener;
+
+        opener = new ClassPathOpener(pathname, false,
+                new ClassPathOpener.Consumer() {
+            public boolean processFileBytes(String name, byte[] bytes) {
+                return Main.processFileBytes(name, bytes);
+            }
+            public void onException(Exception ex) {
+                if (ex instanceof StopProcessing) {
+                    throw (StopProcessing) ex;
+                }
+                DxConsole.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+                ex.printStackTrace(DxConsole.err);
+                errors++;
+            }
+            public void onProcessArchiveStart(File file) {
+                if (args.verbose) {
+                    DxConsole.out.println("processing archive " + file + "...");
+                }
+            }
+        });
+
+        return opener.process();     
+    }
+
+    /**
+     * Processes one file, which may be either a class or a resource.
+     *
+     * @param name non-null; name of the file
+     * @param bytes non-null; contents of the file
+     * @return whether processing was successful
+     */
+    private static boolean processFileBytes(String name, byte[] bytes) {
+        boolean isClass = name.endsWith(".class");
+        boolean keepResources = (outputResources != null);
+
+        if (!isClass && !keepResources) {
+            if (args.verbose) {
+                DxConsole.out.println("ignored resource " + name);
+            }
+            return false;
+        }
+
+        if (args.verbose) {
+            DxConsole.out.println("processing " + name + "...");
+        }
+
+        String fixedName = fixPath(name);
+
+        if (isClass) {
+            if (keepResources && args.keepClassesInJar) {
+                outputResources.put(fixedName, bytes);
+            }
+            return processClass(fixedName, bytes);
+        } else {
+            outputResources.put(fixedName, bytes);
+            return true;
+        }
+    }
+
+    /**
+     * Processes one classfile.
+     *
+     * @param name non-null; name of the file, clipped such that it
+     * <i>should</i> correspond to the name of the class it contains
+     * @param bytes non-null; contents of the file
+     * @return whether processing was successful
+     */
+    private static boolean processClass(String name, byte[] bytes) {
+        if (! args.coreLibrary) {
+            checkClassName(name);
+        }
+        
+        try {
+            ClassDefItem clazz =
+                CfTranslator.translate(name, bytes, args.cfOptions);
+            outputDex.add(clazz);
+            return true;
+        } catch (ParseException ex) {
+            DxConsole.err.println("\ntrouble processing:");
+            if (args.debug) {
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                ex.printContext(DxConsole.err);
+            }
+        }
+
+        warnings++;
+        return false;
+    }
+
+    /**
+     * Check the class name to make sure it's not a "core library"
+     * class. If there is a problem, this updates the error count and
+     * throws an exception to stop processing.
+     * 
+     * @param name non-null; the fully-qualified internal-form class name
+     */
+    private static void checkClassName(String name) {
+        boolean bogus = false;
+        
+        if (name.startsWith("java/")) {
+            bogus = true;
+        } else if (name.startsWith("javax/")) {
+            int slashAt = name.indexOf('/', 6);
+            if (slashAt == -1) {
+                // Top-level javax classes are verboten.
+                bogus = true;
+            } else {
+                String pkg = name.substring(6, slashAt);
+                bogus = (Arrays.binarySearch(JAVAX_CORE, pkg) >= 0);
+            }
+        }
+
+        if (! bogus) {
+            return;
+        }
+
+        /*
+         * The user is probably trying to include an entire desktop
+         * core library in a misguided attempt to get their application
+         * working. Try to help them understand what's happening.
+         */
+
+        DxConsole.err.println("\ntrouble processing \"" + name + "\":");
+        DxConsole.err.println("\n" + 
+                "Attempt to include a core VM class in something other " +
+                "than a core library.\n" +
+                "It is likely that you have attempted to include the " +
+                "core library from a desktop\n" +
+                "virtual machine into an application, which will most " +
+                "assuredly not work. If\n" +
+                "you really intend to build a core library -- which is "+
+                "only appropriate as\n" +
+                "part of creating a full virtual machine binary, as " +
+                "opposed to compiling an\n" +
+                "application -- then use the \"--core-library\" option " +
+                "to suppress this error\n" +
+                "message. If you go ahead and use \"--core-library\" " +
+                "but are in fact building\n" +
+                "an application, then please be aware that your build " +
+                "will still fail at some\n" +
+                "point; you will simply be denied the pleasure of " +
+                "reading this helpful error\n" +
+                "message.");
+        errors++;
+        throw new StopProcessing();
+    }
+
+    /**
+     * Converts {@link #outputDex} into a <code>byte[]</code>, write
+     * it out to the proper file (if any), and also do whatever human-oriented
+     * dumping is required.
+     *
+     * @return null-ok; the converted <code>byte[]</code> or <code>null</code>
+     * if there was a problem
+     */
+    private static byte[] writeDex() {
+        byte[] outArray = null;
+
+        try {
+            OutputStream out = null;
+            OutputStream humanOutRaw = null;
+            OutputStreamWriter humanOut = null;
+            try {
+                if (args.humanOutName != null) {
+                    humanOutRaw = openOutput(args.humanOutName);
+                    humanOut = new OutputStreamWriter(humanOutRaw);
+                }
+
+                if (args.methodToDump != null) {
+                    /*
+                     * Simply dump the requested method. Note: The call
+                     * to toDex() is required just to get the underlying
+                     * structures ready.
+                     */
+                    outputDex.toDex(null, false);
+                    dumpMethod(outputDex, args.methodToDump, humanOut);
+                } else {
+                    /*
+                     * This is the usual case: Create an output .dex file,
+                     * and write it, dump it, etc.
+                     */
+                    outArray = outputDex.toDex(humanOut, args.verboseDump);
+
+                    if ((args.outName != null) && !args.jarOutput) {
+                        out = openOutput(args.outName);
+                        out.write(outArray);
+                    }
+                }
+
+                if (args.statistics) {
+                    DxConsole.out.println(outputDex.getStatistics().toHuman());
+                }
+            } finally {
+                if (humanOut != null) {
+                    humanOut.flush();
+                }
+                closeOutput(out);
+                closeOutput(humanOutRaw);
+            }
+        } catch (Exception ex) {
+            if (args.debug) {
+                DxConsole.err.println("\ntrouble writing output:");
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                DxConsole.err.println("\ntrouble writing output: " +
+                                   ex.getMessage());
+            }
+            return null;
+        }
+
+        return outArray;
+    }
+
+    /**
+     * Creates a jar file from the resources and given dex file array.
+     *
+     * @param fileName non-null; name of the file
+     * @param dexArray non-null; array containing the dex file to include
+     * @return whether the creation was successful
+     */
+    private static boolean createJar(String fileName, byte[] dexArray) {
+        /*
+         * Make or modify the manifest (as appropriate), put the dex
+         * array into the resources map, and then process the entire
+         * resources map in a uniform manner.
+         */
+
+        try {
+            Manifest manifest = makeManifest();
+            OutputStream out = openOutput(fileName);
+            JarOutputStream jarOut = new JarOutputStream(out, manifest);
+
+            outputResources.put(DEX_IN_JAR_NAME, dexArray);
+
+            try {
+                for (Map.Entry<String, byte[]> e :
+                         outputResources.entrySet()) {
+                    String name = e.getKey();
+                    byte[] contents = e.getValue();
+                    JarEntry entry = new JarEntry(name);
+
+                    if (args.verbose) {
+                        DxConsole.out.println("writing " + name + "; size " +
+                                           contents.length + "...");
+                    }
+
+                    entry.setSize(contents.length);
+                    jarOut.putNextEntry(entry);
+                    jarOut.write(contents);
+                    jarOut.closeEntry();
+                }
+            } finally {
+                jarOut.finish();
+                jarOut.flush();
+                closeOutput(out);
+            }
+        } catch (Exception ex) {
+            if (args.debug) {
+                DxConsole.err.println("\ntrouble writing output:");
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                DxConsole.err.println("\ntrouble writing output: " +
+                                   ex.getMessage());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Creates and returns the manifest to use for the output. This may
+     * modify {@link #outputResources} (removing the pre-existing manifest).
+     *
+     * @return non-null; the manifest
+     */
+    private static Manifest makeManifest() throws IOException {
+        byte[] manifestBytes = outputResources.get(MANIFEST_NAME);
+        Manifest manifest;
+        Attributes attribs;
+
+        if (manifestBytes == null) {
+            // We need to construct an entirely new manifest.
+            manifest = new Manifest();
+            attribs = manifest.getMainAttributes();
+            attribs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        } else {
+            manifest = new Manifest(new ByteArrayInputStream(manifestBytes));
+            attribs = manifest.getMainAttributes();
+            outputResources.remove(MANIFEST_NAME);
+        }
+
+        String createdBy = attribs.getValue(CREATED_BY);
+        if (createdBy == null) {
+            createdBy = "";
+        } else {
+            createdBy += " + ";
+        }
+        createdBy += "dx " + Version.VERSION;
+
+        attribs.put(CREATED_BY, createdBy);
+        attribs.putValue("Dex-Location", DEX_IN_JAR_NAME);
+
+        return manifest;
+    }
+
+    /**
+     * Opens and returns the named file for writing, treating "-" specially.
+     *
+     * @param name non-null; the file name
+     * @return non-null; the opened file
+     */
+    private static OutputStream openOutput(String name) throws IOException {
+        if (name.equals("-") ||
+                name.startsWith("-.")) {
+            return System.out;
+        }
+
+        return new FileOutputStream(name);
+    }
+
+    /**
+     * Flushes and closes the given output stream, except if it happens to be
+     * {@link System#out} in which case this method does the flush but not
+     * the close. This method will also silently do nothing if given a
+     * <code>null</code> argument.
+     *
+     * @param stream null-ok; what to close
+     */
+    private static void closeOutput(OutputStream stream) throws IOException {
+        if (stream == null) {
+            return;
+        }
+
+        stream.flush();
+
+        if (stream != System.out) {
+            stream.close();
+        }
+    }
+
+    /**
+     * Returns the "fixed" version of a given file path, suitable for
+     * use as a path within a <code>.jar</code> file and for checking
+     * against a classfile-internal "this class" name. This looks for
+     * the last instance of the substring <code>"/./"</code> within
+     * the path, and if it finds it, it takes the portion after to be
+     * the fixed path. If that isn't found but the path starts with
+     * <code>"./"</code>, then that prefix is removed and the rest is
+     * return. If neither of these is the case, this method returns
+     * its argument.
+     *
+     * @param path non-null; the path to "fix"
+     * @return non-null; the fixed version (which might be the same as
+     * the given <code>path</code>)
+     */
+    private static String fixPath(String path) {
+        /*
+         * If the path separator is \ (like on windows), we convert the
+         * path to a standard '/' separated path.
+         */
+        if (File.separatorChar == '\\') {
+            path = path.replace('\\', '/');
+        }
+
+        int index = path.lastIndexOf("/./");
+
+        if (index != -1) {
+            return path.substring(index + 3);
+        }
+
+        if (path.startsWith("./")) {
+            return path.substring(2);
+        }
+
+        return path;
+    }
+
+    /**
+     * Dumps any method with the given name in the given file.
+     *
+     * @param dex non-null; the dex file
+     * @param fqName non-null; the fully-qualified name of the method(s)
+     * @param out non-null; where to dump to
+     */
+    private static void dumpMethod(DexFile dex, String fqName,
+            OutputStreamWriter out) {
+        boolean wildcard = fqName.endsWith("*");
+        int lastDot = fqName.lastIndexOf('.');
+
+        if ((lastDot <= 0) || (lastDot == (fqName.length() - 1))) {
+            DxConsole.err.println("bogus fully-qualified method name: " +
+                               fqName);
+            return;
+        }
+
+        String className = fqName.substring(0, lastDot).replace('.', '/');
+        String methodName = fqName.substring(lastDot + 1);
+        ClassDefItem clazz = dex.getClassOrNull(className);
+
+        if (clazz == null) {
+            DxConsole.err.println("no such class: " + className);
+            return;
+        }
+
+        if (wildcard) {
+            methodName = methodName.substring(0, methodName.length() - 1);
+        }
+
+        ArrayList<EncodedMethod> allMeths = clazz.getMethods();
+        TreeMap<CstNat, EncodedMethod> meths =
+            new TreeMap<CstNat, EncodedMethod>();
+
+        /*
+         * Figure out which methods to include in the output, and get them
+         * all sorted, so that the printout code is robust with respect to
+         * changes in the underlying order.
+         */
+        for (EncodedMethod meth : allMeths) {
+            String methName = meth.getName().getString();
+            if ((wildcard && methName.startsWith(methodName)) ||
+                (!wildcard && methName.equals(methodName))) {
+                meths.put(meth.getRef().getNat(), meth);
+            }
+        }
+
+        if (meths.size() == 0) {
+            DxConsole.err.println("no such method: " + fqName);
+            return;
+        }
+
+        PrintWriter pw = new PrintWriter(out);
+
+        for (EncodedMethod meth : meths.values()) {
+            // TODO: Better stuff goes here, perhaps.
+            meth.debugPrint(pw, args.verboseDump);
+
+            /*
+             * The (default) source file is an attribute of the class, but 
+             * it's useful to see it in method dumps.
+             */
+            CstUtf8 sourceFile = clazz.getSourceFile();
+            if (sourceFile != null) {
+                pw.println("  source file: " + sourceFile.toQuoted());
+            }
+
+            Annotations methodAnnotations =
+                clazz.getMethodAnnotations(meth.getRef());
+            AnnotationsList parameterAnnotations =
+                clazz.getParameterAnnotations(meth.getRef());
+
+            if (methodAnnotations != null) {
+                pw.println("  method annotations:");
+                for (Annotation a : methodAnnotations.getAnnotations()) {
+                    pw.println("    " + a);
+                }
+            }
+
+            if (parameterAnnotations != null) {
+                pw.println("  parameter annotations:");
+                int sz = parameterAnnotations.size();
+                for (int i = 0; i < sz; i++) {
+                    pw.println("    parameter " + i);
+                    Annotations annotations = parameterAnnotations.get(i);
+                    for (Annotation a : annotations.getAnnotations()) {
+                        pw.println("      " + a);
+                    }
+                }
+            }
+        }
+
+        pw.flush();
+    }
+
+    /**
+     * Exception class used to halt processing prematurely.
+     */
+    private static class StopProcessing extends RuntimeException {
+        // This space intentionally left blank.
+    }
+    
+    /**
+     * Command-line argument parser and access.
+     */
+    public static class Arguments {
+        /** whether to run in debug mode */
+        public boolean debug = false;
+
+        /** whether to emit high-level verbose human-oriented output */
+        public boolean verbose = false;
+
+        /** whether to emit verbose human-oriented output in the dump file */
+        public boolean verboseDump = false;
+
+        /** whether we are constructing a core library */
+        public boolean coreLibrary = false;
+
+        /** null-ok; particular method to dump */
+        public String methodToDump = null;
+
+        /** max width for columnar output */
+        public int dumpWidth = 0;
+
+        /** null-ok; output file name for binary file */
+        public String outName = null;
+
+        /** null-ok; output file name for human-oriented dump */
+        public String humanOutName = null;
+
+        /** whether strict file-name-vs-class-name checking should be done */
+        public boolean strictNameCheck = true;
+
+        /**
+         * whether it is okay for there to be no <code>.class</code> files
+         * to process
+         */
+        public boolean emptyOk = false;
+
+        /**
+         * whether the binary output is to be a <code>.jar</code> file
+         * instead of a plain <code>.dex</code>
+         */
+        public boolean jarOutput = false;
+
+        /**
+         * when writing a <code>.jar</code> file, whether to still
+         * keep the <code>.class</code> files
+         */
+        public boolean keepClassesInJar = false;
+
+        /** how much source position info to preserve */
+        public int positionInfo = PositionList.LINES;
+
+        /** whether to keep local variable information */
+        public boolean localInfo = true;
+
+        /** non-null after {@link #parse}; file name arguments */
+        public String[] fileNames;
+
+        /** whether to do SSA/register optimization */
+        public boolean optimize = true;
+
+        /** Filename containg list of methods to optimize */
+        public String optimizeListFile = null;
+
+        /** Filename containing list of methods to NOT optimize */
+        public String dontOptimizeListFile = null;
+
+        /** Whether to print statistics to stdout at end of compile cycle */
+        public boolean statistics;
+
+        /** Options for dex.cf.* */
+        public CfOptions cfOptions;
+
+        /**
+         * Parses the given command-line arguments.
+         *
+         * @param args non-null; the arguments
+         */
+        public void parse(String[] args) {
+            int at = 0;
+
+            for (/*at*/; at < args.length; at++) {
+                String arg = args[at];
+                if (arg.equals("--") || !arg.startsWith("--")) {
+                    break;
+                } else if (arg.equals("--debug")) {
+                    debug = true;
+                } else if (arg.equals("--verbose")) {
+                    verbose = true;
+                } else if (arg.equals("--verbose-dump")) {
+                    verboseDump = true;
+                } else if (arg.equals("--no-files")) {
+                    emptyOk = true;
+                } else if (arg.equals("--no-optimize")) {
+                    optimize = false;
+                } else if (arg.equals("--no-strict")) {
+                    strictNameCheck = false;
+                } else if (arg.equals("--core-library")) {
+                    coreLibrary = true;
+                } else if (arg.equals("--statistics")) {
+                    statistics = true;
+                } else if (arg.startsWith("--optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    optimizeListFile = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.startsWith("--no-optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    dontOptimizeListFile = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--keep-classes")) {
+                    keepClassesInJar = true;
+                } else if (arg.startsWith("--output=")) {
+                    outName = arg.substring(arg.indexOf('=') + 1);
+                    if (outName.endsWith(".zip") ||
+                            outName.endsWith(".jar") ||
+                            outName.endsWith(".apk")) {
+                        jarOutput = true;
+                    } else if (outName.endsWith(".dex") ||
+                               outName.equals("-")) {
+                        jarOutput = false;
+                    } else {
+                        System.err.println("unknown output extension: " +
+                                           outName);
+                        throw new UsageException();
+                    }
+                } else if (arg.startsWith("--dump-to=")) {
+                    humanOutName = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.startsWith("--dump-width=")) {
+                    arg = arg.substring(arg.indexOf('=') + 1);
+                    dumpWidth = Integer.parseInt(arg);
+                } else if (arg.startsWith("--dump-method=")) {
+                    methodToDump = arg.substring(arg.indexOf('=') + 1);
+                    jarOutput = false;
+                } else if (arg.startsWith("--positions=")) {
+                    String pstr = arg.substring(arg.indexOf('=') + 1).intern();
+                    if (pstr == "none") {
+                        positionInfo = PositionList.NONE;
+                    } else if (pstr == "important") {
+                        positionInfo = PositionList.IMPORTANT;
+                    } else if (pstr == "lines") {
+                        positionInfo = PositionList.LINES;
+                    } else {
+                        System.err.println("unknown positions option: " +
+                                           pstr);
+                        throw new UsageException();
+                    }
+                } else if (arg.equals("--no-locals")) {
+                    localInfo = false;
+                } else {
+                    System.err.println("unknown option: " + arg);
+                    throw new UsageException();
+                }
+            }
+
+            int fileCount = args.length - at;
+            fileNames = new String[fileCount];
+            System.arraycopy(args, at, fileNames, 0, fileCount);
+
+            if (fileCount == 0) {
+                if (!emptyOk) {
+                    System.err.println("no input files specified");
+                    throw new UsageException();
+                }
+            } else if (emptyOk) {
+                System.out.println("ignoring input files");
+                at = args.length;
+            }
+
+            if ((humanOutName == null) && (methodToDump != null)) {
+                humanOutName = "-";
+            }
+
+            makeCfOptions();
+        }
+
+        /**
+         * Copies relevent arguments over into a CfOptions instance.
+         */
+        private void makeCfOptions() {
+            cfOptions = new CfOptions();
+
+            cfOptions.positionInfo = positionInfo;
+            cfOptions.localInfo = localInfo;
+            cfOptions.strictNameCheck = strictNameCheck;
+            cfOptions.optimize = optimize;
+            cfOptions.optimizeListFile = optimizeListFile;
+            cfOptions.dontOptimizeListFile = dontOptimizeListFile;
+            cfOptions.statistics = statistics;
+            cfOptions.warn = DxConsole.err;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/Args.java b/dx/src/com/android/dx/command/dump/Args.java
new file mode 100644
index 0000000..042fae2
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Args.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+/**
+ * contains command line parsedArgs values
+ */
+class Args {
+    /** whether to run in debug mode */
+    boolean debug = false;
+
+    /** whether to dump raw bytes where salient */
+    boolean rawBytes = false;
+
+    /** whether to dump information about basic blocks */
+    boolean basicBlocks = false;
+
+    /** whether to dump regiserized blocks */
+    boolean ropBlocks = false;
+
+    /** whether to dump SSA-form blocks */
+    boolean ssaBlocks = false;
+
+    /** Step in SSA processing to stop at, or null for all */
+    String ssaStep = null;
+
+    /** whether to run SSA optimizations */
+    boolean optimize = false;
+
+    /** whether to be strict about parsing classfiles*/
+    boolean strictParse = false;
+
+    /** max width for columnar output */
+    int width = 0;
+
+    /** whether to dump flow-graph in "dot" format */
+    boolean dotDump = false;
+
+    /** if non-null, an explicit method to dump */
+    String method;
+
+}
diff --git a/dx/src/com/android/dx/command/dump/BaseDumper.java b/dx/src/com/android/dx/command/dump/BaseDumper.java
new file mode 100644
index 0000000..f4a8dee
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BaseDumper.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IndentingWriter;
+import com.android.dx.util.TwoColumnOutput;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+
+/**
+ * Base class for the various human-friendly dumpers.
+ */
+public abstract class BaseDumper
+        implements ParseObserver {
+    /** non-null; array of data being dumped */
+    private final byte[] bytes;
+
+    /** whether or not to include the raw bytes (in a column on the left) */
+    private final boolean rawBytes;
+
+    /** non-null; where to dump to */
+    private final PrintStream out;
+
+    /** width of the output in columns */
+    private final int width;
+
+    /**
+     * non-null; the file path for the class, excluding any base directory
+     * specification 
+     */
+    private final String filePath;
+
+    /** whether to be strict about parsing */
+    private final boolean strictParse;
+
+     /** number of bytes per line in hex dumps */
+    private final int hexCols;
+
+    /** the current level of indentation */
+    private int indent;
+
+    /** non-null; the current column separator string */
+    private String separator;
+
+    /** the offset of the next byte to dump */
+    private int at;
+
+    /** commandline parsedArgs */
+    protected Args args;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bytes non-null; bytes of the (alleged) class file
+     * on the left)
+     * @param out non-null; where to dump to
+     * passed in as &lt;= 0
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     */
+    public BaseDumper(byte[] bytes, PrintStream out,
+                      String filePath, Args args) {
+        this.bytes = bytes;
+        this.rawBytes = args.rawBytes;
+        this.out = out;
+        this.width = (args.width <= 0) ? 79 : args.width;
+        this.filePath = filePath;
+        this.strictParse = args.strictParse;
+        this.indent = 0;
+        this.separator = rawBytes ? "|" : "";
+        this.at = 0;
+        this.args = args;
+
+        int hexCols = (((width - 5) / 15) + 1) & ~1;
+        if (hexCols < 6) {
+            hexCols = 6;
+        } else if (hexCols > 10) {
+            hexCols = 10;
+        }
+        this.hexCols = hexCols;
+    }
+
+    /**
+     * Computes the total width, in register-units, of the parameters for
+     * this method.
+     * @param meth method to process
+     * @return width in register-units
+     */
+    static int computeParamWidth(ConcreteMethod meth, boolean isStatic) {
+        return meth.getEffectiveDescriptor().getParameterTypes().getWordCount();
+    }
+
+    /** {@inheritDoc} */
+    public void changeIndent(int indentDelta) {
+        indent += indentDelta;
+
+        separator = rawBytes ? "|" : "";
+        for (int i = 0; i < indent; i++) {
+            separator += "  ";
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        offset = bytes.underlyingOffset(offset, getBytes());
+
+        boolean rawBytes = getRawBytes();
+
+        if (offset < at) {
+            println("<dump skipped backwards to " + Hex.u4(offset) + ">");
+            at = offset;
+        } else if (offset > at) {
+            String hex = rawBytes ? hexDump(at, offset - at) : "";
+            print(twoColumns(hex, "<skipped to " + Hex.u4(offset) + ">"));
+            at = offset;
+        }
+
+        String hex = rawBytes ? hexDump(offset, len) : "";
+        print(twoColumns(hex, human));
+        at += len;
+    }
+
+    /** {@inheritDoc} */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the current dump cursor (that is, the offset of the expected
+     * next byte to dump).
+     * 
+     * @return &gt;= 0; the dump cursor
+     */
+    protected final int getAt() {
+        return at;
+    }
+
+    /**
+     * Sets the dump cursor to the indicated offset in the given array.
+     * 
+     * @param arr non-null; array in question
+     * @param offset &gt;= 0; offset into the array
+     */
+    protected final void setAt(ByteArray arr, int offset) {
+        at = arr.underlyingOffset(offset, bytes);
+    }
+
+    /**
+     * Gets the array of <code>byte</code>s to process.
+     * 
+     * @return non-null; the bytes
+     */
+    protected final byte[] getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the filesystem/jar path of the file being dumped.
+     * 
+     * @return non-null; the path
+     */
+    protected final String getFilePath() {
+        return filePath;
+    }
+
+    /**
+     * Gets whether to be strict about parsing.
+     * 
+     * @return whether to be strict about parsing
+     */
+    protected final boolean getStrictParse() {
+        return strictParse;
+    }
+
+    /**
+     * Prints the given string to this instance's output stream.
+     * 
+     * @param s null-ok; string to print
+     */
+    protected final void print(String s) {
+        out.print(s);
+    }
+
+    /**
+     * Prints the given string to this instance's output stream, followed
+     * by a newline.
+     * 
+     * @param s null-ok; string to print
+     */
+    protected final void println(String s) {
+        out.println(s);
+    }
+
+    /**
+     * Gets whether this dump is to include raw bytes.
+     * 
+     * @return the raw bytes flag
+     */
+    protected final boolean getRawBytes() {
+        return rawBytes;
+    }
+
+    /**
+     * Gets the width of the first column of output. This is <code>0</code>
+     * unless raw bytes are being included in the output.
+     * 
+     * @return &gt;= 0; the width of the first column
+     */
+    protected final int getWidth1() {
+        if (rawBytes) {
+            return 5 + (hexCols * 2) + (hexCols / 2);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Gets the width of the second column of output.
+     * 
+     * @return &gt;= 0; the width of the second column
+     */
+    protected final int getWidth2() {
+        int w1 = rawBytes ? (getWidth1() + 1) : 0;
+        return width - w1 - (indent * 2);
+    }
+
+    /**
+     * Constructs a hex data dump of the given portion of {@link #bytes}.
+     * 
+     * @param offset offset to start dumping at
+     * @param len length to dump
+     * @return non-null; the dump
+     */
+    protected final String hexDump(int offset, int len) {
+        return Hex.dump(bytes, offset, len, offset, hexCols, 4);
+    }
+
+    /**
+     * Combines a pair of strings as two columns, or if this is one-column
+     * output, format the otherwise-second column.
+     * 
+     * @param s1 non-null; the first column's string
+     * @param s2 non-null; the second column's string
+     * @return non-null; the combined output
+     */
+    protected final String twoColumns(String s1, String s2) {
+        int w1 = getWidth1();
+        int w2 = getWidth2();
+
+        try {
+            if (w1 == 0) {
+                int len2 = s2.length();
+                StringWriter sw = new StringWriter(len2 * 2);
+                IndentingWriter iw = new IndentingWriter(sw, w2, separator);
+
+                iw.write(s2);
+                if ((len2 == 0) || (s2.charAt(len2 - 1) != '\n')) {
+                    iw.write('\n');
+                }
+                iw.flush();
+
+                return sw.toString();
+            } else {
+                return TwoColumnOutput.toString(s1, w1, separator, s2, w2);
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/BlockDumper.java b/dx/src/com/android/dx/command/dump/BlockDumper.java
new file mode 100644
index 0000000..0be2fb4
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BlockDumper.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.code.BasicBlocker;
+import com.android.dx.cf.code.ByteBlock;
+import com.android.dx.cf.code.ByteBlockList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.CodeObserver;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump basic block info from methods in a human-friendly form.
+ */
+public class BlockDumper
+        extends BaseDumper {
+    /** whether or not to registerize (make rop blocks) */
+    private boolean rop;
+
+    /**
+     * null-ok; the class file object being constructed; becomes non-null
+     * during {@link #dump} 
+     */
+    protected DirectClassFile classFile;
+
+    /** null-ok; most recently parsed code attribute */
+    private AttCode codeAtt;
+
+    /** whether or not to suppress dumping */
+    protected boolean suppressDump;
+
+    /** whether this is the first method being dumped */
+    private boolean first;
+
+    /** whether or not to run the ssa optimziations */
+    private boolean optimize;
+
+    /**
+     * Dumps the given array, interpreting it as a class file and dumping
+     * methods with indications of block-level stuff.
+     * 
+     * @param bytes non-null; bytes of the (alleged) class file
+     * @param out non-null; where to dump to
+     * passed in as &lt;= 0
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param rop whether or not to registerize (make rop blocks)
+     * @param args commandline parsedArgs
+     */
+    public static void dump(byte[] bytes, PrintStream out,
+                            String filePath, boolean rop, Args args) {
+        BlockDumper bd =
+            new BlockDumper(bytes, out, filePath,
+                            rop, args);
+        bd.dump();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable.
+     * Use {@link #dump}.
+     */
+    BlockDumper(byte[] bytes, PrintStream out,
+                        String filePath,
+                        boolean rop, Args args) {
+        super(bytes, out, filePath, args);
+
+        this.rop = rop;
+        this.classFile = null;
+        this.codeAtt = null;
+        this.suppressDump = true;
+        this.first = true;
+        this.optimize = args.optimize;
+    }
+
+    /**
+     * Does the dumping.
+     */
+    public void dump() {
+        byte[] bytes = getBytes();
+        ByteArray ba = new ByteArray(bytes);
+
+        /*
+         * First, parse the file completely, so we can safely refer to
+         * attributes, etc.
+         */
+        classFile = new DirectClassFile(ba, getFilePath(), getStrictParse());
+        classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        classFile.getMagic(); // Force parsing to happen.
+
+        // Next, reparse it and observe the process.
+        DirectClassFile liveCf =
+            new DirectClassFile(ba, getFilePath(), getStrictParse());
+        liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        liveCf.setObserver(this);
+        liveCf.getMagic(); // Force parsing to happen.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void changeIndent(int indentDelta) {
+        if (!suppressDump) {
+            super.changeIndent(indentDelta);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        if (!suppressDump) {
+            super.parsed(bytes, offset, len, human);
+        }
+    }
+
+    /**
+     * @param name method name
+     * @return true if this method should be dumped
+     */
+    protected boolean shouldDumpMethod(String name) {
+        return args.method == null || args.method.equals(name);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+        if (descriptor.indexOf('(') < 0) {
+            // It's a field, not a method
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        // Reset the dump cursor to the start of the method.
+        setAt(bytes, offset);
+
+        suppressDump = false;
+
+        if (first) {
+            first = false;
+        } else {
+            parsed(bytes, offset, 0, "\n");
+        }
+
+        parsed(bytes, offset, 0, "method " + name + " " + descriptor);
+        suppressDump = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+        
+        ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+                                                 true, true);
+
+        if (rop) {
+            ropDump(meth);
+        } else {
+            regularDump(meth);
+        }
+    }
+
+    /**
+     * Does a regular basic block dump.
+     * 
+     * @param meth non-null; method data to dump
+     */
+    private void regularDump(ConcreteMethod meth) {
+        BytecodeArray code = meth.getCode();
+        ByteArray bytes = code.getBytes();
+        ByteBlockList list = BasicBlocker.identifyBlocks(meth);
+        int sz = list.size();
+        CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this);
+
+        // Reset the dump cursor to the start of the bytecode
+        setAt(bytes, 0);
+
+        suppressDump = false;
+
+        int byteAt = 0;
+        for (int i = 0; i < sz; i++) {
+            ByteBlock bb = list.get(i);
+            int start = bb.getStart();
+            int end = bb.getEnd();
+
+            if (byteAt < start) {
+                parsed(bytes, byteAt, start - byteAt,
+                       "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start));
+            }
+
+            parsed(bytes, start, 0,
+                   "block " + Hex.u2(bb.getLabel()) + ": " +
+                   Hex.u2(start) + ".." + Hex.u2(end));
+            changeIndent(1);
+
+            int len;
+            for (int j = start; j < end; j += len) {
+                len = code.parseInstruction(j, codeObserver);
+                codeObserver.setPreviousOffset(j);
+            }
+
+            IntList successors = bb.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                parsed(bytes, end, 0, "returns");
+            } else {
+                for (int j = 0; j < ssz; j++) {
+                    int succ = successors.get(j);
+                    parsed(bytes, end, 0, "next " + Hex.u2(succ));
+                }
+            }
+
+            ByteCatchList catches = bb.getCatches();
+            int csz = catches.size();
+            for (int j = 0; j < csz; j++) {
+                ByteCatchList.Item one = catches.get(j);
+                CstType exceptionClass = one.getExceptionClass();
+                parsed(bytes, end, 0,
+                       "catch " +
+                       ((exceptionClass == CstType.OBJECT) ? "<any>" : 
+                        exceptionClass.toHuman()) + " -> " +
+                       Hex.u2(one.getHandlerPc()));
+            }
+
+            changeIndent(-1);
+            byteAt = end;
+        }
+
+        int end = bytes.size();
+        if (byteAt < end) {
+            parsed(bytes, byteAt, end - byteAt,
+                   "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end));
+        }
+
+        suppressDump = true;
+    }
+
+    /**
+     * Does a registerizing dump.
+     * 
+     * @param meth non-null; method data to dump
+     */
+    private void ropDump(ConcreteMethod meth) {
+        BytecodeArray code = meth.getCode();
+        ByteArray bytes = code.getBytes();
+
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+
+        RopMethod rmeth =
+            Ropper.convert(meth, advice);
+        StringBuffer sb = new StringBuffer(2000);
+
+        if (optimize) {
+            boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+
+            int paramWidth = computeParamWidth(meth, isStatic);
+            rmeth = Optimizer.optimize(rmeth, paramWidth, isStatic, true,
+                    advice);
+        }
+
+        BasicBlockList blocks = rmeth.getBlocks();
+
+        sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
+
+        int sz = blocks.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock bb = blocks.get(i);
+            int label = bb.getLabel();
+            sb.append("block ");
+            sb.append(Hex.u2(label));
+            sb.append("\n");
+
+            IntList preds = rmeth.labelToPredecessors(label);
+            int psz = preds.size();
+            for (int j = 0; j < psz; j++) {
+                sb.append("  pred ");
+                sb.append(Hex.u2(preds.get(j)));
+                sb.append("\n");
+            }
+
+            InsnList il = bb.getInsns();
+            int ilsz = il.size();
+            for (int j = 0; j < ilsz; j++) {
+                Insn one = il.get(j);
+                sb.append("  ");
+                sb.append(il.get(j).toHuman());
+                sb.append("\n");
+            }
+
+            IntList successors = bb.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                sb.append("  returns\n");
+            } else {
+                int primary = bb.getPrimarySuccessor();
+                for (int j = 0; j < ssz; j++) {
+                    int succ = successors.get(j);
+                    sb.append("  next ");
+                    sb.append(Hex.u2(succ));
+
+                    if ((ssz != 1) && (succ == primary)) {
+                        sb.append(" *");
+                    }
+
+                    sb.append("\n");
+                }
+            }
+        }
+
+        suppressDump = false;
+        setAt(bytes, 0);
+        parsed(bytes, 0, bytes.size(), sb.toString());
+        suppressDump = true;
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/ClassDumper.java b/dx/src/com/android/dx/command/dump/ClassDumper.java
new file mode 100644
index 0000000..10dacf3
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/ClassDumper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.util.ByteArray;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump the contents of class files in a human-friendly form.
+ */
+public final class ClassDumper
+        extends BaseDumper {
+    /**
+     * Dumps the given array, interpreting it as a class file.
+     *
+     * @param bytes non-null; bytes of the (alleged) class file
+     * @param out non-null; where to dump to
+     * passed in as &lt;= 0
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param args bag of commandline arguments
+     */
+    public static void dump(byte[] bytes, PrintStream out,
+                            String filePath, Args args) {
+        ClassDumper cd =
+            new ClassDumper(bytes, out, filePath, args);
+        cd.dump();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable.
+     * Use {@link #dump}.
+     */
+    private ClassDumper(byte[] bytes, PrintStream out,
+                        String filePath, Args args) {
+        super(bytes, out, filePath, args);
+    }
+
+    /**
+     * Does the dumping.
+     */
+    public void dump() {
+        byte[] bytes = getBytes();
+        ByteArray ba = new ByteArray(bytes);
+        DirectClassFile cf =
+            new DirectClassFile(ba, getFilePath(), getStrictParse());
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.setObserver(this);
+        cf.getMagic(); // Force parsing to happen.
+
+        int at = getAt();
+        if (at != bytes.length) {
+            parsed(ba, at, bytes.length - at, "<extra data at end of file>");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/DotDumper.java b/dx/src/com/android/dx/command/dump/DotDumper.java
new file mode 100644
index 0000000..87c5298
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/DotDumper.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Dumps the pred/succ graph of methods into a format compatible
+ * with the popular graph utility "dot".
+ */
+public class DotDumper implements ParseObserver {
+
+    DirectClassFile classFile;
+
+    byte[] bytes;
+    String filePath;
+    boolean strictParse;
+    boolean optimize;
+    Args args;
+
+    static void dump (byte[] bytes, String filePath, Args args) {
+        new DotDumper(bytes, filePath, args).run();
+    }
+
+    DotDumper(byte[] bytes, String filePath, Args args) {
+        this.bytes = bytes;
+        this.filePath = filePath;
+        this.strictParse = args.strictParse;
+        this.optimize = args.optimize;
+        this.args = args;
+    }
+
+
+    private void run() {
+        ByteArray ba = new ByteArray(bytes);
+
+        /*
+         * First, parse the file completely, so we can safely refer to
+         * attributes, etc.
+         */
+        classFile = new DirectClassFile(ba, filePath, strictParse);
+        classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        classFile.getMagic(); // Force parsing to happen.
+
+        // Next, reparse it and observe the process.
+        DirectClassFile liveCf =
+            new DirectClassFile(ba, filePath, strictParse);
+        liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        liveCf.setObserver(this);
+        liveCf.getMagic(); // Force parsing to happen.
+    }
+
+    /**
+     * @param name method name
+     * @return true if this method should be dumped
+     */
+    protected boolean shouldDumpMethod(String name) {
+        return args.method == null || args.method.equals(name);
+    }
+
+    public void changeIndent(int indentDelta) {
+
+    }
+
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        
+    }
+
+    /** {@inheritDoc} */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+
+    }
+
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+                                                 true, true);
+
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+        RopMethod rmeth =
+            Ropper.convert(meth, advice);
+
+        if (optimize) {
+            boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+            rmeth = Optimizer.optimize(rmeth,
+                    BaseDumper.computeParamWidth(meth, isStatic), isStatic,
+                    true, advice);
+        }
+
+        System.out.println("digraph "  + name + "{");
+
+        System.out.println("\tfirst -> n"
+                + Hex.u2(rmeth.getFirstLabel()) + ";");
+
+        BasicBlockList blocks = rmeth.getBlocks();
+
+        int sz = blocks.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock bb = blocks.get(i);
+            int label = bb.getLabel();
+            IntList successors = bb.getSuccessors();
+
+            if (successors.size() == 0) {
+                System.out.println("\tn" + Hex.u2(label) + " -> returns;");
+            } else if (successors.size() == 1) {
+                System.out.println("\tn" + Hex.u2(label) + " -> n"
+                        + Hex.u2(successors.get(0)) + ";");
+            } else {
+                System.out.print("\tn" + Hex.u2(label) + " -> {");
+                for (int j = 0; j < successors.size(); j++ ) {
+                    int successor = successors.get(j);
+
+                    if (successor != bb.getPrimarySuccessor()) {
+                        System.out.print(" n" + Hex.u2(successor) + " ");
+                    }
+
+                }
+                System.out.println("};");
+
+                System.out.println("\tn" + Hex.u2(label) + " -> n"
+                        + Hex.u2(bb.getPrimarySuccessor())
+                        + " [label=\"primary\"];");
+
+
+            }
+        }
+
+        System.out.println("}");
+
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/Main.java b/dx/src/com/android/dx/command/dump/Main.java
new file mode 100644
index 0000000..1ea26bc
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.util.FileUtils;
+import com.android.dx.util.HexParser;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Main class for the class file dumper.
+ */
+public class Main {
+
+    static Args parsedArgs = new Args();
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run!
+     */
+    public static void main(String[] args) {
+        int at = 0;
+
+        for (/*at*/; at < args.length; at++) {
+            String arg = args[at];
+            if (arg.equals("--") || !arg.startsWith("--")) {
+                break;
+            } else if (arg.equals("--bytes")) {
+                parsedArgs.rawBytes = true;
+            } else if (arg.equals("--basic-blocks")) {
+                parsedArgs.basicBlocks = true;
+            } else if (arg.equals("--rop-blocks")) {
+                parsedArgs.ropBlocks = true;
+            } else if (arg.equals("--optimize")) {
+                parsedArgs.optimize = true;
+            } else if (arg.equals("--ssa-blocks")) {
+                parsedArgs.ssaBlocks = true;
+            } else if (arg.startsWith("--ssa-step=")) {
+                parsedArgs.ssaStep = arg.substring(arg.indexOf('=') + 1);
+            } else if (arg.equals("--debug")) {
+                parsedArgs.debug = true;
+            } else if (arg.equals("--dot")) {
+                parsedArgs.dotDump = true;
+            } else if (arg.equals("--strict")) {
+                parsedArgs.strictParse = true;
+            } else if (arg.startsWith("--width=")) {
+                arg = arg.substring(arg.indexOf('=') + 1);
+                parsedArgs.width = Integer.parseInt(arg);
+            } else if (arg.startsWith("--method=")) {
+                arg = arg.substring(arg.indexOf('=') + 1);
+                parsedArgs.method = arg;
+            } else {
+                System.err.println("unknown option: " + arg);
+                throw new RuntimeException("usage");
+            }
+        }
+
+        if (at == args.length) {
+            System.err.println("no input files specified");
+            throw new RuntimeException("usage");
+        }
+
+        for (/*at*/; at < args.length; at++) {
+            try {
+                String name = args[at];
+                System.out.println("reading " + name + "...");
+                byte[] bytes = FileUtils.readFile(name);
+                if (!name.endsWith(".class")) {
+                    String src;
+                    try {
+                        src = new String(bytes, "utf-8");
+                    } catch (UnsupportedEncodingException ex) {
+                        throw new RuntimeException("shouldn't happen", ex);
+                    }
+                    bytes = HexParser.parse(src);
+                }
+                processOne(name, bytes);
+            } catch (ParseException ex) {
+                System.err.println("\ntrouble parsing:");
+                if (parsedArgs.debug) {
+                    ex.printStackTrace();
+                } else {
+                    ex.printContext(System.err);
+                }
+            }
+        }
+    }
+
+    /**
+     * Processes one file.
+     *
+     * @param name non-null; name of the file
+     * @param bytes non-null; contents of the file
+     */
+    private static void processOne(String name, byte[] bytes) {
+        if (parsedArgs.dotDump) {
+            DotDumper.dump(bytes, name, parsedArgs);
+        } else if (parsedArgs.basicBlocks) {
+            BlockDumper.dump(bytes, System.out, name, false, parsedArgs);
+        } else if (parsedArgs.ropBlocks) {
+            BlockDumper.dump(bytes, System.out, name, true, parsedArgs);
+        } else if (parsedArgs.ssaBlocks) {
+            // --optimize ignored with --ssa-blocks
+            parsedArgs.optimize = false;
+            SsaDumper.dump(bytes, System.out, name, parsedArgs);
+        } else {
+            ClassDumper.dump(bytes, System.out, name, parsedArgs);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/SsaDumper.java b/dx/src/com/android/dx/command/dump/SsaDumper.java
new file mode 100644
index 0000000..95442a1
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/SsaDumper.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.DeadCodeRemover;
+import com.android.dx.ssa.PhiTypeResolver;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaConverter;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.ConstCollector;
+import com.android.dx.ssa.SCCP;
+import com.android.dx.ssa.LiteralOpUpgrader;
+import com.android.dx.ssa.back.SsaToRop;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+import java.util.BitSet;
+import java.util.EnumSet;
+
+public class SsaDumper extends BlockDumper {
+
+    public static void dump(byte[] bytes, PrintStream out,
+            String filePath, Args args) {
+
+        SsaDumper sd =
+            new SsaDumper(bytes, out, filePath, args);
+        sd.dump();
+    }
+
+    SsaDumper(byte[] bytes, PrintStream out, String filePath, Args args) {
+
+        super(bytes, out, filePath, true, args);
+
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+                                                 true, true);
+
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+
+        RopMethod rmeth = Ropper.convert(meth, advice);
+
+        SsaMethod ssaMeth = null;
+
+        boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+        int paramWidth = computeParamWidth(meth, isStatic);
+        if (args.ssaStep == null) {
+            ssaMeth = Optimizer.debugNoRegisterAllocation(rmeth,
+                    paramWidth, isStatic, true, advice,
+                    EnumSet.allOf(Optimizer.OptionalStep.class));
+        } else if ("edge-split".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugEdgeSplit(rmeth, paramWidth,
+                    isStatic, true, advice);
+        } else if ("phi-placement".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugPhiPlacement(
+                    rmeth, paramWidth, isStatic, true, advice);
+        } else if ("renaming".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugRenaming(
+                    rmeth, paramWidth, isStatic, true, advice);
+        } else if ("dead-code".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugDeadCodeRemover(
+                    rmeth, paramWidth, isStatic,true, advice);
+        }
+
+        StringBuffer sb = new StringBuffer(2000);
+
+        sb.append("first ");
+        sb.append(Hex.u2(
+                ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex())));
+        sb.append('\n');
+        
+        for (SsaBasicBlock block : ssaMeth.getBlocks()) {
+            sb.append("block ")
+                    .append(Hex.u2(block.getRopLabel())).append('\n');
+
+            BitSet preds = block.getPredecessors();
+
+            for(int i=preds.nextSetBit(0); i>=0; i=preds.nextSetBit(i+1)) {
+                sb.append ("  pred ");
+                sb.append (Hex.u2(ssaMeth.blockIndexToRopLabel(i)));
+                sb.append('\n');
+            }
+
+            sb.append ("  live in:" + block.getLiveInRegs());
+            sb.append ("\n");
+
+            for (SsaInsn insn: block.getInsns()) {
+                sb.append("  ");
+                sb.append(insn.toHuman());
+                sb.append('\n');
+            }
+
+            if (block.getSuccessors().cardinality() == 0) {
+                sb.append ("  returns\n");
+            } else {
+                int primary = block.getPrimarySuccessorRopLabel();
+
+                IntList succLabelList = block.getRopLabelSuccessorList();
+
+                int szSuccLabels = succLabelList.size();
+
+                for (int i = 0; i < szSuccLabels; i++) {
+                    sb.append ("  next ");
+                    sb.append (Hex.u2(succLabelList.get(i)));
+
+                    if (szSuccLabels != 1 && primary == succLabelList.get(i)) {
+                        sb.append (" *");                        
+                    }
+                    sb.append('\n');
+                }
+            }
+
+            sb.append ("  live out:" + block.getLiveOutRegs());
+            sb.append ("\n");
+        }
+
+        suppressDump = false;
+        setAt(bytes, 0);
+        parsed(bytes, 0, bytes.size(), sb.toString());
+        suppressDump = true;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
new file mode 100644
index 0000000..dab15c9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.file.AnnotationUtils;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Warning;
+
+import java.util.ArrayList;
+
+/**
+ * Utility methods that translate various classfile attributes
+ * into forms suitable for use in creating <code>dex</code> files.
+ */
+/*package*/ class AttributeTranslator {
+    /**
+     * This class is uninstantiable.
+     */
+    private AttributeTranslator() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the list of thrown exceptions for a given method.
+     *
+     * @param method non-null; the method in question
+     * @return non-null; the list of thrown exceptions
+     */
+    public static TypeList getExceptions(Method method) {
+        AttributeList attribs = method.getAttributes();
+        AttExceptions exceptions = (AttExceptions)
+            attribs.findFirst(AttExceptions.ATTRIBUTE_NAME);
+
+        if (exceptions == null) {
+            return StdTypeList.EMPTY;
+        }
+
+        return exceptions.getExceptions();
+    }
+
+    /**
+     * Gets the annotations out of a given {@link AttributeList}. This
+     * combines both visible and invisible annotations into a single
+     * result set and also adds in a system annotation for the
+     * <code>Signature</code> attribute if present.
+     * 
+     * @param attribs non-null; the attributes list to search in
+     * @return non-null; the set of annotations, which may be empty
+     */
+    public static Annotations getAnnotations(AttributeList attribs) {
+        Annotations result = getAnnotations0(attribs);
+        Annotation signature = getSignature(attribs);
+
+        if (signature != null) {
+            result = Annotations.combine(result, signature);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the annotations out of a given class, similar to {@link
+     * #getAnnotations}, also including annotations for translations
+     * of class-level attributes <code>EnclosingMethod</code> and
+     * <code>InnerClasses</code>, if present. Additionally, if the
+     * class is an annotation class, then this also includes a
+     * representation of all the <code>AnnotationDefault</code>
+     * values.
+     * 
+     * @param cf non-null; the class in question
+     * @param args non-null; the high-level options
+     * @return non-null; the set of annotations, which may be empty
+     */
+    public static Annotations getClassAnnotations(DirectClassFile cf,
+            CfOptions args) {
+        CstType thisClass = cf.getThisClass();
+        AttributeList attribs = cf.getAttributes();
+        Annotations result = getAnnotations(attribs);
+        Annotation enclosingMethod = translateEnclosingMethod(attribs);
+
+        try {
+            Annotations innerClassAnnotations =
+                translateInnerClasses(thisClass, attribs,
+                        enclosingMethod == null);
+            if (innerClassAnnotations != null) {
+                result = Annotations.combine(result, innerClassAnnotations);
+            }
+        } catch (Warning warn) {
+            args.warn.println("warning: " + warn.getMessage());
+        }
+
+        if (enclosingMethod != null) {
+            result = Annotations.combine(result, enclosingMethod);
+        }
+
+        if (AccessFlags.isAnnotation(cf.getAccessFlags())) {
+            Annotation annotationDefault =
+                translateAnnotationDefaults(cf);
+            if (annotationDefault != null) {
+                result = Annotations.combine(result, annotationDefault);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the annotations out of a given method, similar to {@link
+     * #getAnnotations}, also including an annotation for the translation
+     * of the method-specific attribute <code>Exceptions</code>.
+     * 
+     * @param method non-null; the method in question
+     * @return non-null; the set of annotations, which may be empty
+     */
+    public static Annotations getMethodAnnotations(Method method) {
+        Annotations result = getAnnotations(method.getAttributes());
+        TypeList exceptions = getExceptions(method);
+
+        if (exceptions.size() != 0) {
+            Annotation throwsAnnotation = 
+                AnnotationUtils.makeThrows(exceptions);
+            result = Annotations.combine(result, throwsAnnotation);
+        }
+
+        return result;
+    }
+    
+    /**
+     * Helper method for {@link #getAnnotations} which just gets the
+     * existing annotations, per se.
+     * 
+     * @param attribs non-null; the attributes list to search in
+     * @return non-null; the set of annotations, which may be empty
+     */
+    private static Annotations getAnnotations0(AttributeList attribs) {
+        AttRuntimeVisibleAnnotations visible =
+            (AttRuntimeVisibleAnnotations)
+            attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+        AttRuntimeInvisibleAnnotations invisible =
+            (AttRuntimeInvisibleAnnotations)
+            attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+        if (visible == null) {
+            if (invisible == null) {
+                return Annotations.EMPTY;
+            }
+            return invisible.getAnnotations();
+        }
+
+        if (invisible == null) {
+            return visible.getAnnotations();
+        }
+
+        // Both are non-null, so combine them.
+
+        return Annotations.combine(visible.getAnnotations(),
+                invisible.getAnnotations());
+    }
+
+    /**
+     * Gets the <code>Signature</code> attribute out of a given
+     * {@link AttributeList}, if any, translating it to an annotation.
+     * 
+     * @param attribs non-null; the attributes list to search in
+     * @return null-ok; the converted <code>Signature</code> annotation,
+     * if there was an attribute to translate
+     */
+    private static Annotation getSignature(AttributeList attribs) {
+        AttSignature signature = (AttSignature)
+            attribs.findFirst(AttSignature.ATTRIBUTE_NAME);
+
+        if (signature == null) {
+            return null;
+        }
+
+        return AnnotationUtils.makeSignature(signature.getSignature());
+    }
+
+    /**
+     * Gets the <code>EnclosingMethod</code> attribute out of a given
+     * {@link AttributeList}, if any, translating it to an annotation.
+     * If the class really has an enclosing method, this returns an
+     * <code>EnclosingMethod</code> annotation; if not, this returns
+     * an <code>EnclosingClass</code> annotation.
+     * 
+     * @param attribs non-null; the attributes list to search in
+     * @return null-ok; the converted <code>EnclosingMethod</code> or
+     * <code>EnclosingClass</code> annotation, if there was an
+     * attribute to translate
+     */
+    private static Annotation translateEnclosingMethod(AttributeList attribs) {
+        AttEnclosingMethod enclosingMethod = (AttEnclosingMethod)
+            attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME);
+
+        if (enclosingMethod == null) {
+            return null;
+        }
+
+        CstType enclosingClass = enclosingMethod.getEnclosingClass();
+        CstNat nat = enclosingMethod.getMethod();
+
+        if (nat == null) {
+            /*
+             * Dalvik doesn't use EnclosingMethod annotations unless
+             * there really is an enclosing method. Anonymous classes
+             * are unambiguously identified by having an InnerClass
+             * annotation with an empty name along with an appropriate
+             * EnclosingClass.
+             */
+            return AnnotationUtils.makeEnclosingClass(enclosingClass);
+        }
+
+        return AnnotationUtils.makeEnclosingMethod(
+                new CstMethodRef(enclosingClass, nat));
+    }
+
+    /**
+     * Gets the <code>InnerClasses</code> attribute out of a given
+     * {@link AttributeList}, if any, translating it to one or more of an
+     * <code>InnerClass</code>, <code>EnclosingClass</code>, or
+     * <code>MemberClasses</code> annotation.
+     * 
+     * @param thisClass non-null; type representing the class being processed
+     * @param attribs non-null; the attributes list to search in
+     * @param needEnclosingClass whether to include an
+     * <code>EnclosingClass</code> annotation
+     * @return null-ok; the converted list of annotations, if there
+     * was an attribute to translate
+     */
+    private static Annotations translateInnerClasses(CstType thisClass,
+            AttributeList attribs, boolean needEnclosingClass) {
+        AttInnerClasses innerClasses = (AttInnerClasses)
+            attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME);
+
+        if (innerClasses == null) {
+            return null;
+        }
+
+        /*
+         * Search the list for the element representing the current class
+         * as well as for any named member classes.
+         */
+
+        InnerClassList list = innerClasses.getInnerClasses();
+        int size = list.size();
+        InnerClassList.Item foundThisClass = null;
+        ArrayList<Type> membersList = new ArrayList<Type>();
+
+        for (int i = 0; i < size; i++) {
+            InnerClassList.Item item = list.get(i);
+            CstType innerClass = item.getInnerClass();
+            if (innerClass.equals(thisClass)) {
+                foundThisClass = item;
+            } else if (thisClass.equals(item.getOuterClass())) {
+                membersList.add(innerClass.getClassType());
+            }
+        }
+
+        int membersSize = membersList.size();
+        
+        if ((foundThisClass == null) && (membersSize == 0)) {
+            return null;
+        }
+
+        Annotations result = new Annotations();
+
+        if (foundThisClass != null) {
+            result.add(AnnotationUtils.makeInnerClass(
+                               foundThisClass.getInnerName(),
+                               foundThisClass.getAccessFlags()));
+            if (needEnclosingClass) {
+                CstType outer = foundThisClass.getOuterClass();
+                if (outer == null) {
+                    throw new Warning(
+                            "Ignoring InnerClasses attribute for an " +
+                            "anonymous inner class that doesn't come with " +
+                            "an associated EnclosingMethod attribute. " +
+                            "(This class was probably produced by a broken " +
+                            "compiler.)");
+                }
+                result.add(AnnotationUtils.makeEnclosingClass(
+                                   foundThisClass.getOuterClass()));
+            }
+        }
+
+        if (membersSize != 0) {
+            StdTypeList typeList = new StdTypeList(membersSize);
+            for (int i = 0; i < membersSize; i++) {
+                typeList.set(i, membersList.get(i));
+            }
+            typeList.setImmutable();
+            result.add(AnnotationUtils.makeMemberClasses(typeList));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the parameter annotations out of a given method. This
+     * combines both visible and invisible annotations into a single
+     * result set.
+     * 
+     * @param method non-null; the method in question
+     * @return non-null; the list of annotation sets, which may be empty
+     */
+    public static AnnotationsList getParameterAnnotations(Method method) {
+        AttributeList attribs = method.getAttributes();
+        AttRuntimeVisibleParameterAnnotations visible =
+            (AttRuntimeVisibleParameterAnnotations)
+            attribs.findFirst(
+                    AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME);
+        AttRuntimeInvisibleParameterAnnotations invisible =
+            (AttRuntimeInvisibleParameterAnnotations)
+            attribs.findFirst(
+                    AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME);
+
+        if (visible == null) {
+            if (invisible == null) {
+                return AnnotationsList.EMPTY;
+            }
+            return invisible.getParameterAnnotations();
+        }
+
+        if (invisible == null) {
+            return visible.getParameterAnnotations();
+        }
+
+        // Both are non-null, so combine them.
+
+        return AnnotationsList.combine(visible.getParameterAnnotations(),
+                invisible.getParameterAnnotations());
+    }
+
+    /**
+     * Gets the <code>AnnotationDefault</code> attributes out of a
+     * given class, if any, reforming them as an
+     * <code>AnnotationDefault</code> annotation.
+     * 
+     * @param cf non-null; the class in question
+     * @return null-ok; an appropriately-constructed
+     * <code>AnnotationDefault</code> annotation, if there were any
+     * annotation defaults in the class, or <code>null<code> if not
+     */
+    private static Annotation translateAnnotationDefaults(DirectClassFile cf) {
+        CstType thisClass = cf.getThisClass();
+        MethodList methods = cf.getMethods();
+        int sz = methods.size();
+        Annotation result =
+            new Annotation(thisClass, AnnotationVisibility.EMBEDDED);
+        boolean any = false;
+
+        for (int i = 0; i < sz; i++) {
+            Method one = methods.get(i);
+            AttributeList attribs = one.getAttributes();
+            AttAnnotationDefault oneDefault = (AttAnnotationDefault)
+                attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME);
+
+            if (oneDefault != null) {
+                NameValuePair pair = new NameValuePair(
+                        one.getNat().getName(),
+                        oneDefault.getValue());
+                result.add(pair);
+                any = true;
+            }
+        }
+
+        if (! any) {
+            return null;
+        }
+
+        result.setImmutable();
+        return AnnotationUtils.makeAnnotationDefault(result);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfOptions.java b/dx/src/com/android/dx/dex/cf/CfOptions.java
new file mode 100644
index 0000000..8726223
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfOptions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.PositionList;
+
+import java.io.PrintStream;
+
+/**
+ * A class to contain options passed into dex.cf
+ */
+public class CfOptions {
+    /** how much source position info to preserve */
+    public int positionInfo = PositionList.LINES;
+
+    /** whether to keep local variable information */
+    public boolean localInfo = false;
+
+    /** whether strict file-name-vs-class-name checking should be done */
+    public boolean strictNameCheck = true;
+    
+    /** whether to do SSA/register optimization */
+    public boolean optimize = false;
+
+    /** filename containing list of methods to optimize */
+    public String optimizeListFile = null;
+
+    /** filename containing list of methods <i>not</i> to optimize */
+    public String dontOptimizeListFile = null;
+
+    /** whether to print statistics to stdout at end of compile cycle */
+    public boolean statistics;
+
+    /** where to issue warnings to */
+    public PrintStream warn = System.err;
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfTranslator.java b/dx/src/com/android/dx/dex/cf/CfTranslator.java
new file mode 100644
index 0000000..c48be53
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Field;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.code.RopTranslator;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.EncodedField;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.LocalVariableExtractor;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Static method that turns <code>byte[]</code>s containing Java
+ * classfiles into {@link ClassDefItem} instances.
+ */
+public class CfTranslator {
+    /** set to <code>true</code> to enable development-time debugging code */
+    private static final boolean DEBUG = false;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private CfTranslator() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Takes a <code>byte[]</code>, interprets it as a Java classfile, and
+     * translates it into a {@link ClassDefItem}.
+     *
+     * @param filePath non-null; the file path for the class,
+     * excluding any base directory specification
+     * @param bytes non-null; contents of the file
+     * @param args command-line arguments
+     * @return non-null; the translated class
+     */
+    public static ClassDefItem translate(String filePath, byte[] bytes,
+            CfOptions args) {
+        try {
+            return translate0(filePath, bytes, args);
+        } catch (RuntimeException ex) {
+            String msg = "...while processing " + filePath;
+            throw ExceptionWithContext.withContext(ex, msg);
+        }
+    }
+
+    /**
+     * Performs the main act of translation. This method is separated
+     * from {@link #translate} just to keep things a bit simpler in
+     * terms of exception handling.
+     *
+     * @param filePath non-null; the file path for the class,
+     * excluding any base directory specification
+     * @param bytes non-null; contents of the file
+     * @param args command-line arguments
+     * @return non-null; the translated class
+     */
+    private static ClassDefItem translate0(String filePath, byte[] bytes,
+            CfOptions args) {
+        DirectClassFile cf =
+            new DirectClassFile(bytes, filePath, args.strictNameCheck);
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.getMagic();
+
+        OptimizerOptions.loadOptimizeLists(args.optimizeListFile,
+                args.dontOptimizeListFile);
+
+        // Build up a class to output.
+
+        CstType thisClass = cf.getThisClass();
+        int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
+        CstUtf8 sourceFile = (args.positionInfo == PositionList.NONE) ? null :
+            cf.getSourceFile();
+        ClassDefItem out =
+            new ClassDefItem(thisClass, classAccessFlags,
+                    cf.getSuperclass(), cf.getInterfaces(), sourceFile);
+
+        Annotations classAnnotations =
+            AttributeTranslator.getClassAnnotations(cf, args);
+        if (classAnnotations.size() != 0) {
+            out.setClassAnnotations(classAnnotations);
+        }
+        
+        processFields(cf, out);
+        processMethods(cf, args, out);
+
+        return out;
+    }
+
+    /**
+     * Processes the fields of the given class.
+     *
+     * @param cf non-null; class being translated
+     * @param out non-null; output class
+     */
+    private static void processFields(DirectClassFile cf, ClassDefItem out) {
+        CstType thisClass = cf.getThisClass();
+        FieldList fields = cf.getFields();
+        int sz = fields.size();
+
+        for (int i = 0; i < sz; i++) {
+            Field one = fields.get(i);
+            try {
+                CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
+                int accessFlags = one.getAccessFlags();
+
+                if (AccessFlags.isStatic(accessFlags)) {
+                    TypedConstant constVal = one.getConstantValue();
+                    EncodedField fi = new EncodedField(field, accessFlags);
+                    if (constVal != null) {
+                        constVal = coerceConstant(constVal, field.getType());
+                    }
+                    out.addStaticField(fi, constVal);
+                } else {
+                    EncodedField fi = new EncodedField(field, accessFlags);
+                    out.addInstanceField(fi);
+                }
+
+                Annotations annotations = 
+                    AttributeTranslator.getAnnotations(one.getAttributes());
+                if (annotations.size() != 0) {
+                    out.addFieldAnnotations(field, annotations);
+                }
+            } catch (RuntimeException ex) {
+                String msg = "...while processing " + one.getName().toHuman() +
+                    " " + one.getDescriptor().toHuman();
+                throw ExceptionWithContext.withContext(ex, msg);
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #processFields}, which translates constants into
+     * more specific types if necessary.
+     * 
+     * @param constant non-null; the constant in question
+     * @param type non-null; the desired type
+     */
+    private static TypedConstant coerceConstant(TypedConstant constant,
+            Type type) {
+        Type constantType = constant.getType();
+
+        if (constantType.equals(type)) {
+            return constant;
+        }
+
+        switch (type.getBasicType()) {
+            case Type.BT_BOOLEAN: {
+                return CstBoolean.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_BYTE: {
+                return CstByte.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_CHAR: {
+                return CstChar.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_SHORT: {
+                return CstShort.make(((CstInteger) constant).getValue());
+            }
+            default: {
+                throw new UnsupportedOperationException("can't coerce " +
+                        constant + " to " + type);
+            }
+        }
+    }
+
+    /**
+     * Processes the methods of the given class.
+     *
+     * @param cf non-null; class being translated
+     * @param args non-null; command-line args
+     * @param out non-null; output class
+     */
+    private static void processMethods(DirectClassFile cf,
+            CfOptions args, ClassDefItem out) {
+        CstType thisClass = cf.getThisClass();
+        MethodList methods = cf.getMethods();
+        int sz = methods.size();
+
+        for (int i = 0; i < sz; i++) {
+            Method one = methods.get(i);
+            try {
+                CstMethodRef meth = new CstMethodRef(thisClass, one.getNat());
+                int accessFlags = one.getAccessFlags();
+                boolean isStatic = AccessFlags.isStatic(accessFlags);
+                boolean isPrivate = AccessFlags.isPrivate(accessFlags);
+                boolean isNative = AccessFlags.isNative(accessFlags);
+                boolean isAbstract = AccessFlags.isAbstract(accessFlags);
+                boolean isConstructor = meth.isInstanceInit() ||
+                    meth.isClassInit();
+                DalvCode code;
+
+                if (isNative || isAbstract) {
+                    // There's no code for native or abstract methods.
+                    code = null;
+                } else {
+                    ConcreteMethod concrete =
+                        new ConcreteMethod(one, cf,
+                                (args.positionInfo != PositionList.NONE),
+                                args.localInfo);
+
+                    TranslationAdvice advice;
+
+                    advice = DexTranslationAdvice.THE_ONE;
+
+                    RopMethod rmeth = Ropper.convert(concrete, advice);
+                    RopMethod nonOptRmeth = null;
+                    int paramSize;
+
+                    paramSize = meth.getParameterWordCount(isStatic);
+    
+                    String canonicalName 
+                            = thisClass.getClassType().getDescriptor()
+                                + "." + one.getName().getString();
+
+                    if (args.optimize &&
+                            OptimizerOptions.shouldOptimize(canonicalName)) {
+                        if (DEBUG) {
+                            System.err.println("Optimizing " + canonicalName);
+                        }
+
+                        nonOptRmeth = rmeth;
+                        rmeth = Optimizer.optimize(rmeth,
+                                paramSize, isStatic, args.localInfo, advice);
+
+                        if (DEBUG) {
+                            OptimizerOptions.compareOptimizerStep(nonOptRmeth,
+                                    paramSize, isStatic, args, advice, rmeth);
+                        }
+
+                        if (args.statistics) {
+                            CodeStatistics.updateRopStatistics(
+                                    nonOptRmeth, rmeth);
+                        }
+                    }
+
+                    LocalVariableInfo locals = null;
+
+                    if (args.localInfo) {
+                        locals = LocalVariableExtractor.extract(rmeth);
+                    }
+
+                    code = RopTranslator.translate(rmeth, args.positionInfo,
+                            locals, paramSize);
+
+                    if (args.statistics && nonOptRmeth != null) {
+                        updateDexStatistics(args, rmeth, nonOptRmeth, locals,
+                                paramSize, concrete.getCode().size());
+                    }
+                }
+
+                // Preserve the synchronized flag as its "declared" variant...
+                if (AccessFlags.isSynchronized(accessFlags)) {
+                    accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;
+
+                    /*
+                     * ...but only native methods are actually allowed to be
+                     * synchronized.
+                     */
+                    if (!isNative) {
+                        accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
+                    }
+                }
+                
+                if (isConstructor) {
+                    accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
+                }
+
+                TypeList exceptions = AttributeTranslator.getExceptions(one);
+                EncodedMethod mi =
+                    new EncodedMethod(meth, accessFlags, code, exceptions);
+
+                if (meth.isInstanceInit() || meth.isClassInit() ||
+                    isStatic || isPrivate) {
+                    out.addDirectMethod(mi);
+                } else {
+                    out.addVirtualMethod(mi);
+                }
+
+                Annotations annotations = 
+                    AttributeTranslator.getMethodAnnotations(one);
+                if (annotations.size() != 0) {
+                    out.addMethodAnnotations(meth, annotations);
+                }
+
+                AnnotationsList list = 
+                    AttributeTranslator.getParameterAnnotations(one);
+                if (list.size() != 0) {
+                    out.addParameterAnnotations(meth, list);
+                }
+            } catch (RuntimeException ex) {
+                String msg = "...while processing " + one.getName().toHuman() +
+                    " " + one.getDescriptor().toHuman();
+                throw ExceptionWithContext.withContext(ex, msg);
+            }
+        }
+    }
+
+    /**
+     * Helper that updates the dex statistics.
+     */
+    private static void updateDexStatistics(CfOptions args,
+            RopMethod optRmeth, RopMethod nonOptRmeth,
+            LocalVariableInfo locals, int paramSize, int originalByteCount) {
+        /*
+         * Run rop->dex again on optimized vs. non-optimized method to
+         * collect statistics. We have to totally convert both ways,
+         * since converting the "real" method getting added to the
+         * file would corrupt it (by messing with its constant pool
+         * indices).
+         */
+
+        DalvCode optCode = RopTranslator.translate(optRmeth,
+                args.positionInfo, locals, paramSize);
+        DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
+                args.positionInfo, locals, paramSize);
+
+        /*
+         * Fake out the indices, so code.getInsns() can work well enough
+         * for the current purpose.
+         */
+
+        DalvCode.AssignIndicesCallback callback = 
+            new DalvCode.AssignIndicesCallback() {
+                public int getIndex(Constant cst) {
+                    // Everything is at index 0!
+                    return 0;
+                }
+            };
+
+        optCode.assignIndices(callback);
+        nonOptCode.assignIndices(callback);
+
+        CodeStatistics.updateDexStatistics(nonOptCode, optCode);
+        CodeStatistics.updateOriginalByteCount(originalByteCount);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CodeStatistics.java b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
new file mode 100644
index 0000000..fa83100
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.RopMethod;
+
+import java.io.PrintStream;
+
+/**
+ * Static methods and variables for collecting statistics on generated
+ * code.
+ */
+public final class CodeStatistics {
+    /** set to <code>true</code> to enable development-time debugging code */
+    private static final boolean DEBUG = false;
+
+    /**
+     * running sum of the number of registers added/removed in
+     * SSA form by the optimizer
+     */
+    public static int runningDeltaRegisters = 0;
+
+    /**
+     * running sum of the number of insns added/removed in
+     * SSA form by the optimizer
+     */
+    public static int runningDeltaInsns = 0;
+
+    /** running sum of the total number of Rop insns processed */
+    public static int runningTotalInsns = 0;
+
+    /**
+     * running sum of the number of dex-form registers added/removed in
+     * SSA form by the optimizer. Only valid if args.statistics is true.
+     */
+    public static int dexRunningDeltaRegisters = 0;
+
+    /**
+     * running sum of the number of dex-form insns (actually code
+     * units) added/removed in SSA form by the optimizer. Only valid
+     * if args.statistics is true.
+     */
+    public static int dexRunningDeltaInsns = 0;
+
+    /**
+     * running sum of the total number of dex insns (actually code
+     * units) processed
+     */
+    public static int dexRunningTotalInsns = 0;
+
+    /** running sum of original class bytecode bytes */
+    public static int runningOriginalBytes = 0;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private CodeStatistics() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Updates the number of original bytecode bytes processed.
+     * 
+     * @param count &gt;= 0; the number of bytes to add
+     */
+    public static void updateOriginalByteCount(int count) {
+        runningOriginalBytes += count;
+    }
+
+    /**
+     * Updates the dex statistics.
+     * 
+     * @param nonOptCode non-optimized code block
+     * @param code optimized code block
+     */
+    public static void updateDexStatistics(DalvCode nonOptCode,
+            DalvCode code) {
+        if (DEBUG) {
+            System.err.println("dex insns (old/new) "
+                    + nonOptCode.getInsns().codeSize()
+                    + "/" + code.getInsns().codeSize()
+                    + " regs (o/n) "
+                    + nonOptCode.getInsns().getRegistersSize()
+                    + "/" + code.getInsns().getRegistersSize()
+            );
+        }
+
+        dexRunningDeltaInsns
+            += (code.getInsns().codeSize()
+                - nonOptCode.getInsns().codeSize());
+
+        dexRunningDeltaRegisters
+            += (code.getInsns().getRegistersSize()
+                - nonOptCode.getInsns().getRegistersSize());
+
+        dexRunningTotalInsns += code.getInsns().codeSize();
+    }
+
+    /**
+     * Updates the ROP statistics.
+     *
+     * @param nonOptRmeth non-optimized method
+     * @param rmeth optimized method
+     */
+    public static void updateRopStatistics(RopMethod nonOptRmeth,
+            RopMethod rmeth) {
+        int oldCountInsns
+                = nonOptRmeth.getBlocks().getEffectiveInstructionCount();
+        int oldCountRegs = nonOptRmeth.getBlocks().getRegCount();
+
+        if (DEBUG) {
+            System.err.println("insns (old/new): "
+                    + oldCountInsns + "/"
+                    + rmeth.getBlocks().getEffectiveInstructionCount()
+                    + " regs (o/n):" + oldCountRegs
+                    + "/"  +  rmeth.getBlocks().getRegCount());
+        }
+
+        int newCountInsns
+                = rmeth.getBlocks().getEffectiveInstructionCount();
+
+        runningDeltaInsns
+            += (newCountInsns - oldCountInsns);
+
+        runningDeltaRegisters
+            += (rmeth.getBlocks().getRegCount() - oldCountRegs);
+
+        runningTotalInsns += newCountInsns;
+    }
+
+    /**
+     * Prints out the collected statistics.
+     * 
+     * @param out non-null; where to output to
+     */
+    public static void dumpStatistics(PrintStream out) {
+        out.printf("Optimizer Delta Rop Insns: %d total: %d "
+                + "(%.2f%%) Delta Registers: %d\n",
+                runningDeltaInsns,
+                runningTotalInsns,
+                (100.0 * (((float) runningDeltaInsns)
+                        / (runningTotalInsns + Math.abs(runningDeltaInsns)))),
+                runningDeltaRegisters);
+
+        out.printf("Optimizer Delta Dex Insns: Insns: %d total: %d "
+                + "(%.2f%%) Delta Registers: %d\n",
+                dexRunningDeltaInsns,
+                dexRunningTotalInsns,
+                (100.0 * (((float) dexRunningDeltaInsns)
+                        / (dexRunningTotalInsns
+                                + Math.abs(dexRunningDeltaInsns)))),
+                dexRunningDeltaRegisters);
+
+        out.printf("Original bytecode byte count: %d\n",
+                runningOriginalBytes);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/OptimizerOptions.java b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
new file mode 100644
index 0000000..1dc3f76
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.Optimizer;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.HashSet;
+
+/**
+ * Settings for optimization of code.
+ */
+public class OptimizerOptions {
+    /**
+     * null-ok; hash set of class name + method names that should be optimized.
+     * null if this constraint was not specified on the command line
+     */
+    private static HashSet<String> optimizeList;
+
+    /**
+     * null-ok; hash set of class name + method names that should NOT
+     * be optimized.  null if this constraint was not specified on the
+     * command line
+     */
+    private static HashSet<String> dontOptimizeList;
+
+    /** true if the above lists have been loaded */
+    private static boolean optimizeListsLoaded;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private OptimizerOptions() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Loads the optimize/don't optimize lists from files.
+     * 
+     * @param optimizeListFile Pathname
+     * @param dontOptimizeListFile Pathname
+     */
+    public static void loadOptimizeLists(String optimizeListFile,
+            String dontOptimizeListFile) {
+        if (optimizeListsLoaded) {
+            return;
+        }
+
+        if (optimizeListFile != null && dontOptimizeListFile != null) {
+            /*
+             * We shouldn't get this far. The condition should have
+             * been caught in the arg processor.
+             */
+            throw new RuntimeException("optimize and don't optimize lists "
+                    + " are mutually exclusive.");
+        }
+
+        if (optimizeListFile != null) {
+            optimizeList = loadStringsFromFile(optimizeListFile);
+        }
+
+        if (dontOptimizeListFile != null) {
+            dontOptimizeList = loadStringsFromFile(dontOptimizeListFile);
+        }
+
+        optimizeListsLoaded = true;
+    }
+
+    /**
+     * Loads a list of newline-separated strings into a new HashSet and returns
+     * the HashSet.
+     * 
+     * @param filename filename to process
+     * @return set of all unique lines in the file
+     */
+    private static HashSet<String> loadStringsFromFile(String filename) {
+        HashSet<String> result = new HashSet<String>();
+
+        try {
+            FileReader fr = new FileReader(filename);
+
+            BufferedReader bfr = new BufferedReader(fr);
+
+            String line;
+
+            while (null != (line = bfr.readLine())) {
+                result.add(line);
+            }
+        } catch (IOException ex) {
+            // Let the exception percolate up as a RuntimeException.
+            throw new RuntimeException("Error with optimize list: " +
+                    filename, ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Compares the output of the optimizer run normally with a run skipping
+     * some optional steps. Results are printed to stderr.
+     *
+     * @param nonOptRmeth non-null; origional rop method
+     * @param paramSize &gt;= 0 parameter size of method
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param args non-null; translator arguments
+     * @param advice non-null; translation advice
+     * @param rmeth non-null; method with all optimization steps run.
+     */
+    public static void compareOptimizerStep(RopMethod nonOptRmeth,
+            int paramSize, boolean isStatic, CfOptions args,
+            TranslationAdvice advice, RopMethod rmeth) {
+        EnumSet<Optimizer.OptionalStep> steps;
+
+        steps = EnumSet.allOf(Optimizer.OptionalStep.class);
+
+        // This is the step to skip.
+        steps.remove(Optimizer.OptionalStep.CONST_COLLECTOR);
+
+        RopMethod skipRopMethod
+                = Optimizer.optimize(nonOptRmeth,
+                        paramSize, isStatic, args.localInfo, advice, steps);
+
+        int normalInsns
+                = rmeth.getBlocks().getEffectiveInstructionCount();
+        int skipInsns
+                = skipRopMethod.getBlocks().getEffectiveInstructionCount();
+
+        System.err.printf(
+                "optimize step regs:(%d/%d/%.2f%%)"
+                + " insns:(%d/%d/%.2f%%)\n",
+                rmeth.getBlocks().getRegCount(),
+                skipRopMethod.getBlocks().getRegCount(),
+                100.0 * ((skipRopMethod.getBlocks().getRegCount()
+                        - rmeth.getBlocks().getRegCount())
+                        / (float) skipRopMethod.getBlocks().getRegCount()),
+                normalInsns, skipInsns,
+                100.0 * ((skipInsns - normalInsns) / (float) skipInsns));
+    }
+
+    /**
+     * Checks whether the specified method should be optimized
+     *
+     * @param canonicalMethodName name of method being considered
+     * @return true if it should be optimized
+     */
+    public static boolean shouldOptimize(String canonicalMethodName) {
+        // Optimize only what's in the optimize list.
+        if (optimizeList != null) {
+            return optimizeList.contains(canonicalMethodName);
+        }
+
+        /*
+         * Or don't optimize what's listed here. (The two lists are
+         * mutually exclusive.
+         */
+
+        if (dontOptimizeList != null) {
+            return !dontOptimizeList.contains(canonicalMethodName);
+        }
+
+        // If neither list has been specified, then optimize everything.
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/package.html b/dx/src/com/android/dx/dex/cf/package.html
new file mode 100644
index 0000000..d56e8a7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/package.html
@@ -0,0 +1,15 @@
+<body>
+<p>Classes for translating Java classfiles into Dalvik classes.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.code</code></li>
+<li><code>com.android.dx.cf.direct</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.dex.code</code></li>
+<li><code>com.android.dx.dex.file</code></li>
+<li><code>com.android.dx.rop.code</code></li>
+<li><code>com.android.dx.rop.cst</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/dex/code/ArrayData.java b/dx/src/com/android/dx/dex/code/ArrayData.java
new file mode 100644
index 0000000..8476a03
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ArrayData.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.*;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.rop.type.Type;
+import java.util.ArrayList;
+
+/**
+ * Pseudo-instruction which holds fill array data. 
+ */
+public final class ArrayData extends VariableSizeInsn {
+    /**
+     * non-null; address representing the instruction that uses this
+     * instance 
+     */
+    private final CodeAddress user;
+
+    /** non-null; initial values to be filled into an array */
+    private final ArrayList<Constant> values;
+
+    /** non-null: type of constant that initializes the array */
+    private final Constant arrayType;
+
+    /** Width of the init value element */
+    private final int elemWidth;
+
+    /** Length of the init list */
+    private final int initLength;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param user non-null; address representing the instruction that
+     * uses this instance
+     * @param values non-null; initial values to be filled into an array
+     */
+    public ArrayData(SourcePosition position, CodeAddress user,
+                     ArrayList<Constant> values,
+                     Constant arrayType) {
+        super(position, RegisterSpecList.EMPTY);
+
+        if (user == null) {
+            throw new NullPointerException("user == null");
+        }
+
+        if (values == null) {
+            throw new NullPointerException("values == null");
+        }
+
+        int sz = values.size();
+
+        if (sz <= 0) {
+            throw new IllegalArgumentException("Illegal number of init values");
+        }
+
+        this.arrayType = arrayType;
+
+        if (arrayType == CstType.BYTE_ARRAY ||
+                arrayType == CstType.BOOLEAN_ARRAY) {
+            elemWidth = 1;
+        } else if (arrayType == CstType.SHORT_ARRAY ||
+                arrayType == CstType.CHAR_ARRAY) {
+            elemWidth = 2;
+        } else if (arrayType == CstType.INT_ARRAY ||
+                arrayType == CstType.FLOAT_ARRAY) {
+            elemWidth = 4;
+        } else if (arrayType == CstType.LONG_ARRAY ||
+                arrayType == CstType.DOUBLE_ARRAY) {
+            elemWidth = 8;
+        } else {
+            throw new IllegalArgumentException("Unexpected constant type");
+        }
+        this.user = user;
+        this.values = values;
+        initLength = values.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        int sz = initLength;
+        // Note: the unit here is 16-bit
+        return 4 + ((sz * elemWidth) + 1) / 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        int baseAddress = user.getAddress();
+        int sz = values.size();
+
+        out.writeShort(0x300 | DalvOps.NOP);
+        out.writeShort(elemWidth);
+        out.writeInt(initLength);
+
+
+        // For speed reasons, replicate the for loop in each case
+        switch (elemWidth) {
+            case 1: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeByte((byte) ((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 2: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeShort((short) ((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 4: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeInt(((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 8: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeLong(((CstLiteral64) cst).getLongBits());
+                }
+                break;
+            }
+            default:
+                break;
+        }
+
+        // Pad one byte to make the size of data table multiples of 16-bits
+        if (elemWidth == 1 && (sz % 2 != 0)) {
+            out.writeByte(0x00);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new ArrayData(getPosition(), user, values, arrayType);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        int sz = values.size();
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n    ");
+            sb.append(i);
+            sb.append(": ");
+            sb.append(values.get(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int baseAddress = user.getAddress();
+        StringBuffer sb = new StringBuffer(100);
+        int sz = values.size();
+
+        sb.append("array-data // for fill-array-data @ ");
+        sb.append(Hex.u2(baseAddress));
+
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n  ");
+            sb.append(i);
+            sb.append(": ");
+            sb.append(values.get(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/BlockAddresses.java b/dx/src/com/android/dx/dex/code/BlockAddresses.java
new file mode 100644
index 0000000..9fea66c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/BlockAddresses.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Container for the set of {@link CodeAddress} instances associated with
+ * the blocks of a particular method. Each block has a corresponding
+ * start address, end address, and last instruction address.
+ */
+public final class BlockAddresses {
+    /** non-null; array containing addresses for the start of each basic
+     * block (indexed by basic block label) */
+    private final CodeAddress[] starts;
+
+    /** non-null; array containing addresses for the final instruction
+     * of each basic block (indexed by basic block label) */
+    private final CodeAddress[] lasts;
+
+    /** non-null; array containing addresses for the end (just past the
+     * final instruction) of each basic block (indexed by basic block
+     * label) */
+    private final CodeAddress[] ends;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method non-null; the method to have block addresses for
+     */
+    public BlockAddresses(RopMethod method) {
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.starts = new CodeAddress[maxLabel];
+        this.lasts = new CodeAddress[maxLabel];
+        this.ends = new CodeAddress[maxLabel];
+
+        setupArrays(method);
+    }
+
+    /**
+     * Gets the instance for the start of the given block.
+     * 
+     * @param block non-null; the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getStart(BasicBlock block) {
+        return starts[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the start of the block with the given label.
+     * 
+     * @param label non-null; the label of the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getStart(int label) {
+        return starts[label];
+    }
+
+    /**
+     * Gets the instance for the final instruction of the given block.
+     * 
+     * @param block non-null; the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getLast(BasicBlock block) {
+        return lasts[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the final instruction of the block with
+     * the given label.
+     * 
+     * @param label non-null; the label of the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getLast(int label) {
+        return lasts[label];
+    }
+
+    /**
+     * Gets the instance for the end (address after the final instruction)
+     * of the given block.
+     * 
+     * @param block non-null; the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getEnd(BasicBlock block) {
+        return ends[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the end (address after the final instruction)
+     * of the block with the given label.
+     * 
+     * @param label non-null; the label of the block in question
+     * @return non-null; the appropriate instance
+     */
+    public CodeAddress getEnd(int label) {
+        return ends[label];
+    }
+
+    /**
+     * Sets up the address arrays.
+     */
+    private void setupArrays(RopMethod method) {
+        BasicBlockList blocks = method.getBlocks();
+        int sz = blocks.size();
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            int label = one.getLabel();
+            Insn insn = one.getInsns().get(0);
+
+            starts[label] = new CodeAddress(insn.getPosition());
+
+            SourcePosition pos = one.getLastInsn().getPosition();
+
+            lasts[label] = new CodeAddress(pos);
+            ends[label] = new CodeAddress(pos);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchBuilder.java b/dx/src/com/android/dx/dex/code/CatchBuilder.java
new file mode 100644
index 0000000..c49b4c7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Constructor of {@link CatchTable} instances from {@link RopMethod}
+ * and associated data.
+ */
+public final class CatchBuilder {
+    /** the maximum range of a single catch handler, in code units */
+    private static final int MAX_CATCH_RANGE = 65535;
+    
+    /** non-null; method to build the list for */
+    private final RopMethod method;
+
+    /** non-null; block output order */
+    private final int[] order;
+
+    /** non-null; address objects for each block */
+    private final BlockAddresses addresses;
+    
+    /**
+     * Constructs an instance. It merely holds onto its parameters for
+     * a subsequent call to {@link #build}.
+     * 
+     * @param method non-null; method to build the list for
+     * @param order non-null; block output order
+     * @param addresses non-null; address objects for each block
+     */
+    public CatchBuilder(RopMethod method, int[] order,
+            BlockAddresses addresses) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (order == null) {
+            throw new NullPointerException("order == null");
+        }
+
+        if (addresses == null) {
+            throw new NullPointerException("addresses == null");
+        }
+
+        this.method = method;
+        this.order = order;
+        this.addresses = addresses;
+    }
+
+    /**
+     * Builds and returns the catch table for this instance.
+     * 
+     * @return non-null; the constructed table
+     */
+    public CatchTable build() {
+        return build(method, order, addresses);
+    }
+
+    /**
+     * Gets whether this instance has any catches at all (either typed
+     * or catch-all).
+     * 
+     * @return whether this instance has any catches at all
+     */
+    public boolean hasAnyCatches() {
+        HashSet<Type> result = new HashSet<Type>(20);
+        BasicBlockList blocks = method.getBlocks();
+        int size = blocks.size();
+        
+        for (int i = 0; i < size; i++) {
+            BasicBlock block = blocks.get(i);
+            TypeList catches = block.getLastInsn().getCatches();
+            if (catches.size() != 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+    
+    /**
+     * Gets the set of catch types associated with this instance.
+     * 
+     * @return non-null; the set of catch types
+     */
+    public HashSet<Type> getCatchTypes() {
+        HashSet<Type> result = new HashSet<Type>(20);
+        BasicBlockList blocks = method.getBlocks();
+        int size = blocks.size();
+        
+        for (int i = 0; i < size; i++) {
+            BasicBlock block = blocks.get(i);
+            TypeList catches = block.getLastInsn().getCatches();
+            int catchSize = catches.size();
+
+            for (int j = 0; j < catchSize; j++) {
+                result.add(catches.getType(j));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds and returns the catch table for a given method.
+     * 
+     * @param method non-null; method to build the list for
+     * @param order non-null; block output order
+     * @param addresses non-null; address objects for each block
+     * @return non-null; the constructed table
+     */
+    public static CatchTable build(RopMethod method, int[] order,
+            BlockAddresses addresses) {
+        int len = order.length;
+        BasicBlockList blocks = method.getBlocks();
+        ArrayList<CatchTable.Entry> resultList =
+            new ArrayList<CatchTable.Entry>(len);
+        CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
+        BasicBlock currentStartBlock = null;
+        BasicBlock currentEndBlock = null;
+        
+        for (int i = 0; i < len; i++) {
+            BasicBlock block = blocks.labelToBlock(order[i]);
+
+            if (!block.canThrow()) {
+                /*
+                 * There is no need to concern ourselves with the
+                 * placement of blocks that can't throw with respect
+                 * to the blocks that *can* throw.
+                 */
+                continue;
+            }
+
+            CatchHandlerList handlers = handlersFor(block, addresses);
+
+            if (currentHandlers.size() == 0) {
+                // This is the start of a new catch range.
+                currentStartBlock = block;
+                currentEndBlock = block;
+                currentHandlers = handlers;
+                continue;
+            }
+
+            if (currentHandlers.equals(handlers)
+                    && rangeIsValid(currentStartBlock, block, addresses)) {
+                /*
+                 * The block we are looking at now has the same handlers
+                 * as the block that started the currently open catch
+                 * range, and adding it to the currently open range won't
+                 * cause it to be too long.
+                 */
+                currentEndBlock = block;
+                continue;
+            }
+
+            /*
+             * The block we are looking at now has incompatible handlers,
+             * so we need to finish off the last entry and start a new
+             * one. Note: We only emit an entry if it has associated handlers.
+             */
+            if (currentHandlers.size() != 0) {
+                CatchTable.Entry entry =
+                    makeEntry(currentStartBlock, currentEndBlock,
+                            currentHandlers, addresses);
+                resultList.add(entry);
+            }
+
+            currentStartBlock = block;
+            currentEndBlock = block;
+            currentHandlers = handlers;
+        }
+
+        if (currentHandlers.size() != 0) {
+            // Emit an entry for the range that was left hanging.
+            CatchTable.Entry entry =
+                makeEntry(currentStartBlock, currentEndBlock,
+                        currentHandlers, addresses);
+            resultList.add(entry);
+        }
+        
+        // Construct the final result.
+
+        int resultSz = resultList.size();
+        
+        if (resultSz == 0) {
+            return CatchTable.EMPTY;
+        }
+
+        CatchTable result = new CatchTable(resultSz);
+
+        for (int i = 0; i < resultSz; i++) {
+            result.set(i, resultList.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Makes the {@link CatchHandlerList} for the given basic block.
+     * 
+     * @param block non-null; block to get entries for
+     * @param addresses non-null; address objects for each block
+     * @return non-null; array of entries
+     */
+    private static CatchHandlerList handlersFor(BasicBlock block,
+            BlockAddresses addresses) {
+        IntList successors = block.getSuccessors();
+        int succSize = successors.size();
+        int primary = block.getPrimarySuccessor();
+        TypeList catches = block.getLastInsn().getCatches();
+        int catchSize = catches.size();
+
+        if (catchSize == 0) {
+            return CatchHandlerList.EMPTY;
+        }
+
+        if (((primary == -1) && (succSize != catchSize))
+                || ((primary != -1) &&
+                        ((succSize != (catchSize + 1))
+                                || (primary != successors.get(catchSize))))) {
+            /*
+             * Blocks that throw are supposed to list their primary
+             * successor -- if any -- last in the successors list, but
+             * that constraint appears to be violated here.
+             */
+            throw new RuntimeException(
+                    "shouldn't happen: weird successors list");
+        }
+
+        /*
+         * Reduce the effective catchSize if we spot a catch-all that
+         * isn't at the end.
+         */
+        for (int i = 0; i < catchSize; i++) {
+            Type type = catches.getType(i);
+            if (type.equals(Type.OBJECT)) {
+                catchSize = i + 1;
+                break;
+            }
+        }
+        
+        CatchHandlerList result = new CatchHandlerList(catchSize);
+
+        for (int i = 0; i < catchSize; i++) {
+            CstType oneType = new CstType(catches.getType(i));
+            CodeAddress oneHandler = addresses.getStart(successors.get(i));
+            result.set(i, oneType, oneHandler.getAddress());
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Makes a {@link CatchTable#Entry} for the given block range and
+     * handlers.
+     *
+     * @param start non-null; the start block for the range (inclusive)
+     * @param end non-null; the start block for the range (also inclusive)
+     * @param handlers non-null; the handlers for the range
+     * @param addresses non-null; address objects for each block
+     */
+    private static CatchTable.Entry makeEntry(BasicBlock start,
+            BasicBlock end, CatchHandlerList handlers,
+            BlockAddresses addresses) {
+        /*
+         * We start at the *last* instruction of the start block, since
+         * that's the instruction that can throw...
+         */
+        CodeAddress startAddress = addresses.getLast(start);
+
+        // ...And we end *after* the last instruction of the end block.
+        CodeAddress endAddress = addresses.getEnd(end);
+
+        return new CatchTable.Entry(startAddress.getAddress(),
+                endAddress.getAddress(), handlers);
+    }
+
+    /**
+     * Gets whether the address range for the given two blocks is valid
+     * for a catch handler. This is true as long as the covered range is
+     * under 65536 code units.
+     * 
+     * @param start non-null; the start block for the range (inclusive)
+     * @param end non-null; the start block for the range (also inclusive)
+     * @param addresses non-null; address objects for each block
+     * @return <code>true</code> if the range is valid as a catch range
+     */
+    private static boolean rangeIsValid(BasicBlock start, BasicBlock end,
+            BlockAddresses addresses) {
+        if (start == null) {
+            throw new NullPointerException("start == null");
+        }
+
+        if (end == null) {
+            throw new NullPointerException("end == null");
+        }
+        
+        // See above about selection of instructions.
+        int startAddress = addresses.getLast(start).getAddress();
+        int endAddress = addresses.getEnd(end).getAddress();
+
+        return (endAddress - startAddress) <= MAX_CATCH_RANGE;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchHandlerList.java b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
new file mode 100644
index 0000000..862586c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Ordered list of (exception type, handler address) entries.
+ */
+public final class CatchHandlerList extends FixedSizeList
+        implements Comparable<CatchHandlerList> {
+    /** non-null; empty instance */
+    public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     *
+     * @param size &gt;= 0; the size of the list
+     */
+    public CatchHandlerList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toHuman("", "");
+    }
+
+    /**
+     * Get the human form of this instance, prefixed on each line
+     * with the string.
+     * 
+     * @param prefix non-null; the prefix for every line
+     * @param header non-null; the header for the first line (after the
+     * first prefix)
+     * @return non-null; the human form
+     */
+    public String toHuman(String prefix, String header) {
+        StringBuilder sb = new StringBuilder(100);
+        int size = size();
+
+        sb.append(prefix);
+        sb.append(header);
+        sb.append("catch ");
+
+        for (int i = 0; i < size; i++) {
+            Entry entry = get(i);
+
+            if (i != 0) {
+                sb.append(",\n");
+                sb.append(prefix);
+                sb.append("  ");
+            }
+
+            if ((i == (size - 1)) && catchesAll()) {
+                sb.append("<any>");
+            } else {
+                sb.append(entry.getExceptionType().toHuman());
+            }
+            
+            sb.append(" -> ");
+            sb.append(Hex.u2or4(entry.getHandler()));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns whether or not this instance ends with a "catch-all"
+     * handler.
+     * 
+     * @return <code>true</code> if this instance ends with a "catch-all"
+     * handler or <code>false</code> if not
+     */
+    public boolean catchesAll() {
+        int size = size();
+
+        if (size == 0) {
+            return false;
+        }
+
+        Entry last = get(size - 1);
+        return last.getExceptionType().equals(CstType.OBJECT);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param exceptionType non-null; type of exception handled
+     * @param handler &gt;= 0; exception handler address
+     */
+    public void set(int n, CstType exceptionType, int handler) {
+        set0(n, new Entry(exceptionType, handler));
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param entry non-null; the entry to set at <code>n</code>
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(CatchHandlerList other) {
+        if (this == other) {
+            // Easy out.
+            return 0;
+        }
+
+        int thisSize = size();
+        int otherSize = other.size();
+        int checkSize = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < checkSize; i++) {
+            Entry thisEntry = get(i);
+            Entry otherEntry = other.get(i);
+            int compare = thisEntry.compareTo(otherEntry);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Entry in the list.
+     */
+    public static class Entry implements Comparable<Entry> {
+        /** non-null; type of exception handled */
+        private final CstType exceptionType;
+
+        /** &gt;= 0; exception handler address */
+        private final int handler;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param exceptionType non-null; type of exception handled
+         * @param handler &gt;= 0; exception handler address
+         */
+        public Entry(CstType exceptionType, int handler) {
+            if (handler < 0) {
+                throw new IllegalArgumentException("handler < 0");
+            }
+
+            if (exceptionType == null) {
+                throw new NullPointerException("exceptionType == null");
+            }
+
+            this.handler = handler;
+            this.exceptionType = exceptionType;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            return (handler * 31) + exceptionType.hashCode();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof Entry) {
+                return (compareTo((Entry) other) == 0);
+            }
+
+            return false;
+        }
+        
+        /** {@inheritDoc} */
+        public int compareTo(Entry other) {
+            if (handler < other.handler) {
+                return -1;
+            } else if (handler > other.handler) {
+                return 1;
+            }
+
+            return exceptionType.compareTo(other.exceptionType);
+        }
+        
+        /**
+         * Gets the exception type handled.
+         * 
+         * @return non-null; the exception type
+         */
+        public CstType getExceptionType() {
+            return exceptionType;
+        }
+
+        /**
+         * Gets the handler address.
+         * 
+         * @return &gt;= 0; the handler address
+         */
+        public int getHandler() {
+            return handler;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchTable.java b/dx/src/com/android/dx/dex/code/CatchTable.java
new file mode 100644
index 0000000..86f9c58
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchTable.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Table of catch entries. Each entry includes a range of code
+ * addresses for which it is valid and an associated {@link
+ * CatchHandlerList}.
+ */
+public final class CatchTable extends FixedSizeList
+        implements Comparable<CatchTable> {
+    /** non-null; empty instance */
+    public static final CatchTable EMPTY = new CatchTable(0);
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     *
+     * @param size &gt;= 0; the size of the table
+     */
+    public CatchTable(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param entry non-null; the entry to set at <code>n</code>
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(CatchTable other) {
+        if (this == other) {
+            // Easy out.
+            return 0;
+        }
+
+        int thisSize = size();
+        int otherSize = other.size();
+        int checkSize = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < checkSize; i++) {
+            Entry thisEntry = get(i);
+            Entry otherEntry = other.get(i);
+            int compare = thisEntry.compareTo(otherEntry);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Entry in a catch list.
+     */
+    public static class Entry implements Comparable<Entry> {
+        /** &gt;= 0; start address */
+        private final int start;
+
+        /** &gt; start; end address (exclusive) */
+        private final int end;
+
+        /** non-null; list of catch handlers */
+        private final CatchHandlerList handlers;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param start &gt;= 0; start address 
+         * @param end &gt; start; end address (exclusive)
+         * @param handlers non-null; list of catch handlers
+         */
+        public Entry(int start, int end, CatchHandlerList handlers) {
+            if (start < 0) {
+                throw new IllegalArgumentException("start < 0");
+            }
+
+            if (end <= start) {
+                throw new IllegalArgumentException("end <= start");
+            }
+
+            if (handlers.isMutable()) {
+                throw new IllegalArgumentException("handlers.isMutable()");
+            }
+
+            this.start = start;
+            this.end = end;
+            this.handlers = handlers;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            int hash = (start * 31) + end;
+            hash = (hash * 31) + handlers.hashCode();
+            return hash;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof Entry) {
+                return (compareTo((Entry) other) == 0);
+            }
+
+            return false;
+        }
+        
+        /** {@inheritDoc} */
+        public int compareTo(Entry other) {
+            if (start < other.start) {
+                return -1;
+            } else if (start > other.start) {
+                return 1;
+            }
+            
+            if (end < other.end) {
+                return -1;
+            } else if (end > other.end) {
+                return 1;
+            }
+
+            return handlers.compareTo(other.handlers);
+        }
+        
+        /**
+         * Gets the start address.
+         * 
+         * @return &gt;= 0; the start address
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Gets the end address (exclusive).
+         * 
+         * @return &gt; start; the end address (exclusive)
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the handlers.
+         * 
+         * @return non-null; the handlers
+         */
+        public CatchHandlerList getHandlers() {
+            return handlers;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CodeAddress.java b/dx/src/com/android/dx/dex/code/CodeAddress.java
new file mode 100644
index 0000000..9730913
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CodeAddress.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to track an address within a code
+ * array. Instances are used for such things as branch targets and
+ * exception handler ranges. Its code size is zero, and so instances
+ * do not in general directly wind up in any output (either
+ * human-oriented or binary file).
+ */
+public final class CodeAddress extends ZeroSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     */
+    public CodeAddress(SourcePosition position) {
+        super(position);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisters(RegisterSpecList registers) {
+        return new CodeAddress(getPosition());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        return "code-address";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CstInsn.java b/dx/src/com/android/dx/dex/code/CstInsn.java
new file mode 100644
index 0000000..1bc9021
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CstInsn.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which has a single constant argument in addition
+ * to all the normal instruction information.
+ */
+public final class CstInsn extends FixedSizeInsn {
+    /** non-null; the constant argument for this instruction */
+    private final Constant constant;
+
+    /**
+     * &gt;= -1; the constant pool index for {@link #constant}, or
+     * <code>-1</code> if not yet set 
+     */
+    private int index;
+
+    /**
+     * &gt;= -1; the constant pool index for the class reference in
+     * {@link #constant} if any, or <code>-1</code> if not yet set 
+     */
+    private int classIndex;
+
+    /**
+     * Constructs an instance. The output address of this instance is
+     * initially unknown (<code>-1</code>) as is the constant pool index.
+     * 
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position non-null; source position
+     * @param registers non-null; register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     * @param constant non-null; constant argument
+     */
+    public CstInsn(Dop opcode, SourcePosition position,
+                   RegisterSpecList registers, Constant constant) {
+        super(opcode, position, registers);
+
+        if (constant == null) {
+            throw new NullPointerException("constant == null");
+        }
+
+        this.constant = constant;
+        this.index = -1;
+        this.classIndex = -1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        CstInsn result = 
+            new CstInsn(opcode, getPosition(), getRegisters(), constant);
+
+        if (index >= 0) {
+            result.setIndex(index);
+        }
+        
+        if (classIndex >= 0) {
+            result.setClassIndex(classIndex);
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        CstInsn result =
+            new CstInsn(getOpcode(), getPosition(), registers, constant);
+
+        if (index >= 0) {
+            result.setIndex(index);
+        }
+        
+        if (classIndex >= 0) {
+            result.setClassIndex(classIndex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the constant argument.
+     * 
+     * @return non-null; the constant argument
+     */
+    public Constant getConstant() {
+        return constant;
+    }
+
+    /**
+     * Gets the constant's index. It is only valid to call this after
+     * {@link #setIndex} has been called.
+     * 
+     * @return &gt;= 0; the constant pool index
+     */
+    public int getIndex() {
+        if (index < 0) {
+            throw new RuntimeException("index not yet set for " + constant);
+        }
+
+        return index;
+    }
+
+    /**
+     * Returns whether the constant's index has been set for this instance.
+     * 
+     * @see #setIndex
+     * 
+     * @return <code>true</code> iff the index has been set
+     */
+    public boolean hasIndex() {
+        return (index >= 0);
+    }
+
+    /**
+     * Sets the constant's index. It is only valid to call this method once
+     * per instance.
+     * 
+     * @param index &gt;= 0; the constant pool index
+     */
+    public void setIndex(int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index < 0");
+        }
+
+        if (this.index >= 0) {
+            throw new RuntimeException("index already set");
+        }
+
+        this.index = index;
+    }
+
+    /**
+     * Gets the constant's class index. It is only valid to call this after
+     * {@link #setClassIndex} has been called.
+     * 
+     * @return &gt;= 0; the constant's class's constant pool index
+     */
+    public int getClassIndex() {
+        if (classIndex < 0) {
+            throw new RuntimeException("class index not yet set");
+        }
+
+        return classIndex;
+    }
+
+    /**
+     * Returns whether the constant's class index has been set for this
+     * instance.
+     * 
+     * @see #setClassIndex
+     * 
+     * @return <code>true</code> iff the index has been set
+     */
+    public boolean hasClassIndex() {
+        return (classIndex >= 0);
+    }
+
+    /**
+     * Sets the constant's class index. This is the constant pool index
+     * for the class referred to by this instance's constant. Only
+     * reference constants have a class, so it is only on instances
+     * with reference constants that this method should ever be
+     * called. It is only valid to call this method once per instance.
+     * 
+     * @param index &gt;= 0; the constant's class's constant pool index
+     */
+    public void setClassIndex(int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index < 0");
+        }
+
+        if (this.classIndex >= 0) {
+            throw new RuntimeException("class index already set");
+        }
+
+        this.classIndex = index;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return constant.toHuman();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvCode.java b/dx/src/com/android/dx/dex/code/DalvCode.java
new file mode 100644
index 0000000..cf6af09
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvCode.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Container for all the pieces of a concrete method. Each instance
+ * corresponds to a <code>code</code> structure in a <code>.dex</code> file.
+ */
+public final class DalvCode {
+    /**
+     * how much position info to preserve; one of the static
+     * constants in {@link PositionList} 
+     */
+    private final int positionInfo;
+
+    /**
+     * null-ok; the instruction list, ready for final processing;
+     * nulled out in {@link #finishProcessingIfNecessary}
+     */
+    private OutputFinisher unprocessedInsns;
+
+    /**
+     * non-null; unprocessed catch table;
+     * nulled out in {@link #finishProcessingIfNecessary}
+     */
+    private CatchBuilder unprocessedCatches;
+
+    /**
+     * null-ok; catch table; set in
+     * {@link #finishProcessingIfNecessary} 
+     */
+    private CatchTable catches;
+
+    /**
+     * null-ok; source positions list; set in
+     * {@link #finishProcessingIfNecessary} 
+     */
+    private PositionList positions;
+
+    /**
+     * null-ok; local variable list; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private LocalList locals;
+
+    /**
+     * null-ok; the processed instruction list; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private DalvInsnList insns;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param unprocessedInsns non-null; the instruction list, ready
+     * for final processing
+     * @param unprocessedCatches non-null; unprocessed catch
+     * (exception handler) table
+     */
+    public DalvCode(int positionInfo, OutputFinisher unprocessedInsns,
+            CatchBuilder unprocessedCatches) {
+        if (unprocessedInsns == null) {
+            throw new NullPointerException("unprocessedInsns == null");
+        }
+
+        if (unprocessedCatches == null) {
+            throw new NullPointerException("unprocessedCatches == null");
+        }
+
+        this.positionInfo = positionInfo;
+        this.unprocessedInsns = unprocessedInsns;
+        this.unprocessedCatches = unprocessedCatches;
+        this.catches = null;
+        this.positions = null;
+        this.locals = null;
+        this.insns = null;
+    }
+
+    /**
+     * Finish up processing of the method.
+     */
+    private void finishProcessingIfNecessary() {
+        if (insns != null) {
+            return;
+        }
+
+        insns = unprocessedInsns.finishProcessingAndGetList();
+        positions = PositionList.make(insns, positionInfo);
+        locals = LocalList.make(insns);
+        catches = unprocessedCatches.build();
+
+        // Let them be gc'ed.
+        unprocessedInsns = null;
+        unprocessedCatches = null;
+    }
+
+    /**
+     * Assign indices in all instructions that need them, using the
+     * given callback to perform lookups. This must be called before
+     * {@link #getInsns}.
+     * 
+     * @param callback non-null; callback object
+     */
+    public void assignIndices(AssignIndicesCallback callback) {
+        unprocessedInsns.assignIndices(callback);
+    }
+    
+    /**
+     * Gets whether this instance has any position data to represent.
+     * 
+     * @return <code>true</code> iff this instance has any position
+     * data to represent
+     */
+    public boolean hasPositions() {
+        return (positionInfo != PositionList.NONE)
+            && unprocessedInsns.hasAnyPositionInfo();
+    }
+            
+    /**
+     * Gets whether this instance has any local variable data to represent.
+     * 
+     * @return <code>true</code> iff this instance has any local variable
+     * data to represent
+     */
+    public boolean hasLocals() {
+        return unprocessedInsns.hasAnyLocalInfo();
+    }
+
+    /**
+     * Gets whether this instance has any catches at all (either typed
+     * or catch-all).
+     * 
+     * @return whether this instance has any catches at all
+     */
+    public boolean hasAnyCatches() {
+        return unprocessedCatches.hasAnyCatches();
+    }
+    
+    /**
+     * Gets the set of catch types handled anywhere in the code.
+     * 
+     * @return non-null; the set of catch types
+     */
+    public HashSet<Type> getCatchTypes() {
+        return unprocessedCatches.getCatchTypes();
+    }
+
+    /**
+     * Gets the set of all constants referred to by instructions in
+     * the code.
+     * 
+     * @return non-null; the set of constants
+     */
+    public HashSet<Constant> getInsnConstants() {
+        return unprocessedInsns.getAllConstants();
+    }
+
+    /**
+     * Gets the list of instructions.
+     * 
+     * @return non-null; the instruction list
+     */
+    public DalvInsnList getInsns() {
+        finishProcessingIfNecessary();
+        return insns;
+    }
+
+    /**
+     * Gets the catch (exception handler) table.
+     * 
+     * @return non-null; the catch table
+     */
+    public CatchTable getCatches() {
+        finishProcessingIfNecessary();
+        return catches;
+    }
+
+    /**
+     * Gets the source positions list.
+     * 
+     * @return non-null; the source positions list
+     */
+    public PositionList getPositions() {
+        finishProcessingIfNecessary();
+        return positions;
+    }
+
+    /**
+     * Gets the source positions list.
+     * 
+     * @return non-null; the source positions list
+     */
+    public LocalList getLocals() {
+        finishProcessingIfNecessary();
+        return locals;
+    }
+
+    /**
+     * Class used as a callback for {@link #assignIndices}.
+     */
+    public static interface AssignIndicesCallback {
+        /**
+         * Gets the index for the given constant.
+         * 
+         * @param cst non-null; the constant
+         * @return &gt;= -1; the index or <code>-1</code> if the constant
+         * shouldn't actually be reified with an index
+         */
+        public int getIndex(Constant cst);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsn.java b/dx/src/com/android/dx/dex/code/DalvInsn.java
new file mode 100644
index 0000000..d922193
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsn.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.TwoColumnOutput;
+
+/**
+ * Base class for Dalvik instructions.
+ */
+public abstract class DalvInsn {
+    /**
+     * the actual output address of this instance, if known, or
+     * <code>-1</code> if not 
+     */
+    private int address;
+
+    /** the opcode; one of the constants from {@link Dops} */
+    private final Dop opcode;
+
+    /** non-null; source position */
+    private final SourcePosition position;
+
+    /** non-null; list of register arguments */
+    private final RegisterSpecList registers;
+
+    /**
+     * Makes a move instruction, appropriate and ideal for the given arguments.
+     * 
+     * @param position non-null; source position information
+     * @param dest non-null; destination register
+     * @param src non-null; source register
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static SimpleInsn makeMove(SourcePosition position,
+            RegisterSpec dest, RegisterSpec src) {
+        boolean category1 = dest.getCategory() == 1;
+        boolean reference = dest.getType().isReference();
+        int destReg = dest.getReg();
+        int srcReg = src.getReg();
+        Dop opcode;
+
+        if ((srcReg | destReg) < 16) {
+            opcode = reference ? Dops.MOVE_OBJECT :
+                (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
+        } else if (destReg < 256) {
+            opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
+                (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
+        } else {
+            opcode = reference ? Dops.MOVE_OBJECT_16 :
+                (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
+        }
+
+        return new SimpleInsn(opcode, position,
+                              RegisterSpecList.make(dest, src));
+    }
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * <p><b>Note:</b> In the unlikely event that an instruction takes
+     * absolutely no registers (e.g., a <code>nop</code> or a
+     * no-argument no-result static method call), then the given
+     * register list may be passed as {@link
+     * RegisterSpecList#EMPTY}.</p>
+     * 
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position non-null; source position
+     * @param registers non-null; register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins and outs)
+     */
+    public DalvInsn(Dop opcode, SourcePosition position,
+                    RegisterSpecList registers) {
+        if (opcode == null) {
+            throw new NullPointerException("opcode == null");
+        }
+
+        if (position == null) {
+            throw new NullPointerException("position == null");
+        }
+
+        if (registers == null) {
+            throw new NullPointerException("registers == null");
+        }
+
+        this.address = -1;
+        this.opcode = opcode;
+        this.position = position;
+        this.registers = registers;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(identifierString());
+        sb.append(' ');
+        sb.append(position);
+
+        sb.append(": ");
+        sb.append(opcode.getName());
+
+        boolean needComma = false;
+        if (registers.size() != 0) {
+            sb.append(registers.toHuman(" ", ", ", null));
+            needComma = true;
+        }
+
+        String extra = argString();
+        if (extra != null) {
+            if (needComma) {
+                sb.append(',');
+            }
+            sb.append(' ');
+            sb.append(extra);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets whether the address of this instruction is known.
+     * 
+     * @see #getAddress
+     * @see #setAddress
+     */
+    public final boolean hasAddress() {
+        return (address >= 0);
+    }
+
+    /**
+     * Gets the output address of this instruction, if it is known. This throws
+     * a <code>RuntimeException</code> if it has not yet been set.
+     * 
+     * @see #setAddress
+     * 
+     * @return &gt;= 0; the output address
+     */
+    public final int getAddress() {
+        if (address < 0) {
+            throw new RuntimeException("address not yet known");
+        }
+
+        return address;
+    }
+
+    /**
+     * Gets the opcode.
+     * 
+     * @return non-null; the opcode
+     */
+    public final Dop getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the source position.
+     * 
+     * @return non-null; the source position
+     */
+    public final SourcePosition getPosition() {
+        return position;
+    }
+
+    /**
+     * Gets the register list for this instruction.
+     * 
+     * @return non-null; the registers
+     */
+    public final RegisterSpecList getRegisters() {
+        return registers;
+    }
+
+    /**
+     * Returns whether this instance's opcode uses a result register.
+     * This method is a convenient shorthand for
+     * <code>getOpcode().hasResult()</code>.
+     * 
+     * @return <code>true</code> iff this opcode uses a result register
+     */
+    public final boolean hasResult() {
+        return opcode.hasResult();
+    }
+
+    /**
+     * Gets the minimum distinct registers required for this instruction.
+     * This assumes that the result (if any) can share registers with the
+     * sources (if any), that each source register is unique, and that
+     * (to be explicit here) category-2 values take up two consecutive
+     * registers.
+     * 
+     * @return &gt;= 0; the minimum distinct register requirement
+     */
+    public final int getMinimumRegisterRequirement() {
+        boolean hasResult = hasResult();
+        int regSz = registers.size();
+        int resultRequirement = hasResult ? registers.get(0).getCategory() : 0;
+        int sourceRequirement = 0;
+
+        for (int i = hasResult ? 1 : 0; i < regSz; i++) {
+            sourceRequirement += registers.get(i).getCategory();
+        }
+
+        return Math.max(sourceRequirement, resultRequirement);
+    }
+
+    /**
+     * Gets the instruction prefix required, if any, to use in a high
+     * register transformed version of this instance.
+     * 
+     * @see #hrVersion
+     * 
+     * @return null-ok; the prefix, if any
+     */
+    public DalvInsn hrPrefix() {
+        RegisterSpecList regs = registers;
+        int sz = regs.size();
+
+        if (hasResult()) {
+            if (sz == 1) {
+                return null;
+            }
+            regs = regs.withoutFirst();
+        } else if (sz == 0) {
+            return null;
+        }
+
+        return new HighRegisterPrefix(position, regs);
+    }
+
+    /**
+     * Gets the instruction suffix required, if any, to use in a high
+     * register transformed version of this instance.
+     * 
+     * @see #hrVersion
+     * 
+     * @return null-ok; the suffix, if any
+     */
+    public DalvInsn hrSuffix() {
+        if (hasResult()) {
+            RegisterSpec r = registers.get(0);
+            return makeMove(position, r, r.withReg(0));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the instruction that is equivalent to this one, except that
+     * uses sequential registers starting at <code>0</code> (storing
+     * the result, if any, in register <code>0</code> as well). The
+     * sequence of instructions from {@link #hrPrefix} and {@link
+     * #hrSuffix} (if non-null) surrounding the result of a call to
+     * this method are the high register transformation of this
+     * instance, and it is guaranteed that the number of low registers
+     * used will be the number returned by {@link
+     * #getMinimumRegisterRequirement}.
+     * 
+     * @return non-null; the replacement
+     */
+    public DalvInsn hrVersion() {
+        RegisterSpecList regs = 
+            registers.withSequentialRegisters(0, hasResult());
+        return withRegisters(regs);
+    }
+
+    /**
+     * Gets the short identifier for this instruction. This is its
+     * address, if assigned, or its identity hashcode if not.
+     * 
+     * @return non-null; the identifier
+     */
+    public final String identifierString() {
+        if (address != -1) {
+            return String.format("%04x", address);
+        }
+
+        return Hex.u4(System.identityHashCode(this));
+    }
+
+    /**
+     * Returns the string form of this instance suitable for inclusion in
+     * a human-oriented listing dump. This method will return <code>null</code>
+     * if this instance should not appear in a listing.
+     * 
+     * @param prefix non-null; prefix before the address; each follow-on
+     * line will be indented to match as well
+     * @param width &gt;= 0; the width of the output or <code>0</code> for
+     * unlimited width
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return null-ok; the string form or <code>null</code> if this
+     * instance should not appear in a listing
+     */
+    public final String listingString(String prefix, int width,
+            boolean noteIndices) {
+        String insnPerSe = listingString0(noteIndices);
+
+        if (insnPerSe == null) {
+            return null;
+        }
+
+        String addr = prefix + identifierString() + ": ";
+        int w1 = addr.length();
+        int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
+
+        return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
+    }
+
+    /**
+     * Sets the output address.
+     * 
+     * @param address &gt;= 0; the output address
+     */
+    public final void setAddress(int address) {
+        if (address < 0) {
+            throw new IllegalArgumentException("address < 0");
+        }
+
+        this.address = address;
+    }
+
+    /**
+     * Gets the address immediately after this instance. This is only
+     * calculable if this instance's address is known, and it is equal
+     * to the address plus the length of the instruction format of this
+     * instance's opcode.
+     * 
+     * @return &gt;= 0; the next address
+     */
+    public final int getNextAddress() {
+        return getAddress() + codeSize();
+    }
+
+    /**
+     * Gets the size of this instruction, in 16-bit code units.
+     * 
+     * @return &gt;= 0; the code size of this instruction
+     */
+    public abstract int codeSize();
+
+    /**
+     * Writes this instance to the given output. This method should
+     * never annotate the output.
+     * 
+     * @param out non-null; where to write to
+     */
+    public abstract void writeTo(AnnotatedOutput out);
+
+    /**
+     * Returns an instance that is just like this one, except that its
+     * opcode is replaced by the one given, and its address is reset.
+     * 
+     * @param opcode non-null; the new opcode
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract DalvInsn withOpcode(Dop opcode);
+
+    /**
+     * Returns an instance that is just like this one, except that all
+     * register references have been offset by the given delta, and its
+     * address is reset.
+     * 
+     * @param delta the amount to offset register references by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract DalvInsn withRegisterOffset(int delta);
+
+    /**
+     * Returns an instance that is just like this one, except that the
+     * register list is replaced by the given one, and its address is
+     * reset.
+     * 
+     * @param registers non-null; new register list
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract DalvInsn withRegisters(RegisterSpecList registers);
+
+    /**
+     * Gets the string form for any arguments to this instance. Subclasses
+     * must override this.
+     * 
+     * @return null-ok; the string version of any arguments or
+     * <code>null</code> if there are none
+     */
+    protected abstract String argString();
+
+    /**
+     * Helper for {@link #listingString}, which returns the string
+     * form of this instance suitable for inclusion in a
+     * human-oriented listing dump, not including the instruction
+     * address and without respect for any output formatting. This
+     * method should return <code>null</code> if this instance should
+     * not appear in a listing.
+     * 
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return null-ok; the listing string
+     */
+    protected abstract String listingString0(boolean noteIndices);
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsnList.java b/dx/src/com/android/dx/dex/code/DalvInsnList.java
new file mode 100644
index 0000000..2cd15c6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsnList.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IndentingWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * List of {@link DalvInsn} instances.
+ */
+public final class DalvInsnList extends FixedSizeList {
+
+    /**
+     * The amount of register space, in register units, required for this
+     * code block. This may be greater than the largest observed register+
+     * category because the method this code block exists in may
+     * specify arguments that are unused by the method.
+     */
+    private final int regCount;
+
+    /**
+     * Constructs and returns an immutable instance whose elements are
+     * identical to the ones in the given list, in the same order.
+     * 
+     * @param list non-null; the list to use for elements
+     * @param regCount count, in register-units, of the number of registers
+     * this code block requires.
+     * @return non-null; an appropriately-constructed instance of this
+     * class
+     */
+    public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
+            int regCount) {
+        int size = list.size();
+        DalvInsnList result = new DalvInsnList(size, regCount);
+
+        for (int i = 0; i < size; i++) {
+            result.set(i, list.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+    
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public DalvInsnList(int size, int regCount) {
+        super(size);
+        this.regCount = regCount;
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public DalvInsn get(int n) {
+        return (DalvInsn) get0(n);
+    }
+
+    /**
+     * Sets the instruction at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param insn non-null; the instruction to set at <code>n</code>
+     */
+    public void set(int n, DalvInsn insn) {
+        set0(n, insn);
+    }
+
+    /**
+     * Gets the size of this instance, in 16-bit code units. This will only
+     * return a meaningful result if the instructions in this instance all
+     * have valid addresses.
+     * 
+     * @return &gt;= 0; the size
+     */
+    public int codeSize() {
+        int sz = size();
+
+        if (sz == 0) {
+            return 0;
+        }
+
+        DalvInsn last = get(sz - 1);
+        return last.getNextAddress();
+    }
+
+    /**
+     * Writes all the instructions in this instance to the given output
+     * destination.
+     * 
+     * @param out non-null; where to write to
+     */
+    public void writeTo(AnnotatedOutput out) {
+        int startCursor = out.getCursor();
+        int sz = size();
+
+        if (out.annotates()) {
+            boolean verbose = out.isVerbose();
+            
+            for (int i = 0; i < sz; i++) {
+                DalvInsn insn = (DalvInsn) get0(i);
+                int codeBytes = insn.codeSize() * 2;
+                String s;
+
+                if ((codeBytes != 0) || verbose) {
+                    s = insn.listingString("  ", out.getAnnotationWidth(),
+                            true);
+                } else {
+                    s = null;
+                }
+
+                if (s != null) {
+                    out.annotate(codeBytes, s);
+                } else if (codeBytes != 0) {
+                    out.annotate(codeBytes, "");
+                }
+            }
+        }
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = (DalvInsn) get0(i);
+            try {
+                insn.writeTo(out);
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while writing " + insn);
+            }
+        }
+
+        // Sanity check of the amount written.
+        int written = (out.getCursor() - startCursor) / 2;
+        if (written != codeSize()) {
+            throw new RuntimeException("write length mismatch; expected " +
+                    codeSize() + " but actually wrote " + written);
+        }
+    }
+
+    /**
+     * Gets the minimum required register count implied by this
+     * instance.  This includes any unused parameters that could
+     * potentially be at the top of the register space.
+     * @return &gt;= 0; the required registers size
+     */
+    public int getRegistersSize() {
+        return regCount;
+    }
+
+    /**
+     * Gets the size of the outgoing arguments area required by this
+     * method. This is equal to the largest argument word count of any
+     * method referred to by this instance.
+     * 
+     * @return &gt;= 0; the required outgoing arguments size
+     */
+    public int getOutsSize() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = (DalvInsn) get0(i);
+
+            if (!(insn instanceof CstInsn)) {
+                continue;
+            }
+
+            Constant cst = ((CstInsn) insn).getConstant();
+
+            if (!(cst instanceof CstBaseMethodRef)) {
+                continue;
+            }
+
+            boolean isStatic =
+                (insn.getOpcode().getFamily() == DalvOps.INVOKE_STATIC);
+            int count =
+                ((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
+
+            if (count > result) {
+                result = count;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     * 
+     * @param out non-null; where to dump
+     * @param prefix non-null; prefix to attach to each line of output
+     * @param verbose whether to be verbose; verbose output includes
+     * lines for zero-size instructions and explicit constant pool indices
+     */
+    public void debugPrint(Writer out, String prefix, boolean verbose) {
+        IndentingWriter iw = new IndentingWriter(out, 0, prefix);
+        int sz = size();
+
+        try {
+            for (int i = 0; i < sz; i++) {
+                DalvInsn insn = (DalvInsn) get0(i);
+                String s;
+
+                if ((insn.codeSize() != 0) || verbose) {
+                    s = insn.listingString("", 0, verbose);
+                } else {
+                    s = null;
+                }
+
+                if (s != null) {
+                    iw.write(s);
+                }
+            }
+
+            iw.flush();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     * 
+     * @param out non-null; where to dump
+     * @param prefix non-null; prefix to attach to each line of output
+     * @param verbose whether to be verbose; verbose output includes
+     * lines for zero-size instructions
+     */
+    public void debugPrint(OutputStream out, String prefix, boolean verbose) {
+        Writer w = new OutputStreamWriter(out);
+        debugPrint(w, prefix, verbose);
+
+        try {
+            w.flush();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvOps.java b/dx/src/com/android/dx/dex/code/DalvOps.java
new file mode 100644
index 0000000..7dc648e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvOps.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * All the Dalvik opcode value constants. See the related spec
+ * document for the meaning and instruction format of each opcode.
+ */
+public final class DalvOps {
+    /** pseudo-opcode used for nonstandard format "instructions" */
+    public static final int SPECIAL_FORMAT = -1;
+
+    /** minimum valid opcode value */
+    public static final int MIN_VALUE = -1;
+
+    /** maximum valid opcode value */
+    public static final int MAX_VALUE = 0xff;
+
+    // BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final int NOP = 0x00;
+    public static final int MOVE = 0x01;
+    public static final int MOVE_FROM16 = 0x02;
+    public static final int MOVE_16 = 0x03;
+    public static final int MOVE_WIDE = 0x04;
+    public static final int MOVE_WIDE_FROM16 = 0x05;
+    public static final int MOVE_WIDE_16 = 0x06;
+    public static final int MOVE_OBJECT = 0x07;
+    public static final int MOVE_OBJECT_FROM16 = 0x08;
+    public static final int MOVE_OBJECT_16 = 0x09;
+    public static final int MOVE_RESULT = 0x0a;
+    public static final int MOVE_RESULT_WIDE = 0x0b;
+    public static final int MOVE_RESULT_OBJECT = 0x0c;
+    public static final int MOVE_EXCEPTION = 0x0d;
+    public static final int RETURN_VOID = 0x0e;
+    public static final int RETURN = 0x0f;
+    public static final int RETURN_WIDE = 0x10;
+    public static final int RETURN_OBJECT = 0x11;
+    public static final int CONST_4 = 0x12;
+    public static final int CONST_16 = 0x13;
+    public static final int CONST = 0x14;
+    public static final int CONST_HIGH16 = 0x15;
+    public static final int CONST_WIDE_16 = 0x16;
+    public static final int CONST_WIDE_32 = 0x17;
+    public static final int CONST_WIDE = 0x18;
+    public static final int CONST_WIDE_HIGH16 = 0x19;
+    public static final int CONST_STRING = 0x1a;
+    public static final int CONST_STRING_JUMBO = 0x1b;
+    public static final int CONST_CLASS = 0x1c;
+    public static final int MONITOR_ENTER = 0x1d;
+    public static final int MONITOR_EXIT = 0x1e;
+    public static final int CHECK_CAST = 0x1f;
+    public static final int INSTANCE_OF = 0x20;
+    public static final int ARRAY_LENGTH = 0x21;
+    public static final int NEW_INSTANCE = 0x22;
+    public static final int NEW_ARRAY = 0x23;
+    public static final int FILLED_NEW_ARRAY = 0x24;
+    public static final int FILLED_NEW_ARRAY_RANGE = 0x25;
+    public static final int FILL_ARRAY_DATA = 0x26;
+    public static final int THROW = 0x27;
+    public static final int GOTO = 0x28;
+    public static final int GOTO_16 = 0x29;
+    public static final int GOTO_32 = 0x2a;
+    public static final int PACKED_SWITCH = 0x2b;
+    public static final int SPARSE_SWITCH = 0x2c;
+    public static final int CMPL_FLOAT = 0x2d;
+    public static final int CMPG_FLOAT = 0x2e;
+    public static final int CMPL_DOUBLE = 0x2f;
+    public static final int CMPG_DOUBLE = 0x30;
+    public static final int CMP_LONG = 0x31;
+    public static final int IF_EQ = 0x32;
+    public static final int IF_NE = 0x33;
+    public static final int IF_LT = 0x34;
+    public static final int IF_GE = 0x35;
+    public static final int IF_GT = 0x36;
+    public static final int IF_LE = 0x37;
+    public static final int IF_EQZ = 0x38;
+    public static final int IF_NEZ = 0x39;
+    public static final int IF_LTZ = 0x3a;
+    public static final int IF_GEZ = 0x3b;
+    public static final int IF_GTZ = 0x3c;
+    public static final int IF_LEZ = 0x3d;
+    public static final int UNUSED_3E = 0x3e;
+    public static final int UNUSED_3F = 0x3f;
+    public static final int UNUSED_40 = 0x40;
+    public static final int UNUSED_41 = 0x41;
+    public static final int UNUSED_42 = 0x42;
+    public static final int UNUSED_43 = 0x43;
+    public static final int AGET = 0x44;
+    public static final int AGET_WIDE = 0x45;
+    public static final int AGET_OBJECT = 0x46;
+    public static final int AGET_BOOLEAN = 0x47;
+    public static final int AGET_BYTE = 0x48;
+    public static final int AGET_CHAR = 0x49;
+    public static final int AGET_SHORT = 0x4a;
+    public static final int APUT = 0x4b;
+    public static final int APUT_WIDE = 0x4c;
+    public static final int APUT_OBJECT = 0x4d;
+    public static final int APUT_BOOLEAN = 0x4e;
+    public static final int APUT_BYTE = 0x4f;
+    public static final int APUT_CHAR = 0x50;
+    public static final int APUT_SHORT = 0x51;
+    public static final int IGET = 0x52;
+    public static final int IGET_WIDE = 0x53;
+    public static final int IGET_OBJECT = 0x54;
+    public static final int IGET_BOOLEAN = 0x55;
+    public static final int IGET_BYTE = 0x56;
+    public static final int IGET_CHAR = 0x57;
+    public static final int IGET_SHORT = 0x58;
+    public static final int IPUT = 0x59;
+    public static final int IPUT_WIDE = 0x5a;
+    public static final int IPUT_OBJECT = 0x5b;
+    public static final int IPUT_BOOLEAN = 0x5c;
+    public static final int IPUT_BYTE = 0x5d;
+    public static final int IPUT_CHAR = 0x5e;
+    public static final int IPUT_SHORT = 0x5f;
+    public static final int SGET = 0x60;
+    public static final int SGET_WIDE = 0x61;
+    public static final int SGET_OBJECT = 0x62;
+    public static final int SGET_BOOLEAN = 0x63;
+    public static final int SGET_BYTE = 0x64;
+    public static final int SGET_CHAR = 0x65;
+    public static final int SGET_SHORT = 0x66;
+    public static final int SPUT = 0x67;
+    public static final int SPUT_WIDE = 0x68;
+    public static final int SPUT_OBJECT = 0x69;
+    public static final int SPUT_BOOLEAN = 0x6a;
+    public static final int SPUT_BYTE = 0x6b;
+    public static final int SPUT_CHAR = 0x6c;
+    public static final int SPUT_SHORT = 0x6d;
+    public static final int INVOKE_VIRTUAL = 0x6e;
+    public static final int INVOKE_SUPER = 0x6f;
+    public static final int INVOKE_DIRECT = 0x70;
+    public static final int INVOKE_STATIC = 0x71;
+    public static final int INVOKE_INTERFACE = 0x72;
+    public static final int UNUSED_73 = 0x73;
+    public static final int INVOKE_VIRTUAL_RANGE = 0x74;
+    public static final int INVOKE_SUPER_RANGE = 0x75;
+    public static final int INVOKE_DIRECT_RANGE = 0x76;
+    public static final int INVOKE_STATIC_RANGE = 0x77;
+    public static final int INVOKE_INTERFACE_RANGE = 0x78;
+    public static final int UNUSED_79 = 0x79;
+    public static final int UNUSED_7A = 0x7a;
+    public static final int NEG_INT = 0x7b;
+    public static final int NOT_INT = 0x7c;
+    public static final int NEG_LONG = 0x7d;
+    public static final int NOT_LONG = 0x7e;
+    public static final int NEG_FLOAT = 0x7f;
+    public static final int NEG_DOUBLE = 0x80;
+    public static final int INT_TO_LONG = 0x81;
+    public static final int INT_TO_FLOAT = 0x82;
+    public static final int INT_TO_DOUBLE = 0x83;
+    public static final int LONG_TO_INT = 0x84;
+    public static final int LONG_TO_FLOAT = 0x85;
+    public static final int LONG_TO_DOUBLE = 0x86;
+    public static final int FLOAT_TO_INT = 0x87;
+    public static final int FLOAT_TO_LONG = 0x88;
+    public static final int FLOAT_TO_DOUBLE = 0x89;
+    public static final int DOUBLE_TO_INT = 0x8a;
+    public static final int DOUBLE_TO_LONG = 0x8b;
+    public static final int DOUBLE_TO_FLOAT = 0x8c;
+    public static final int INT_TO_BYTE = 0x8d;
+    public static final int INT_TO_CHAR = 0x8e;
+    public static final int INT_TO_SHORT = 0x8f;
+    public static final int ADD_INT = 0x90;
+    public static final int SUB_INT = 0x91;
+    public static final int MUL_INT = 0x92;
+    public static final int DIV_INT = 0x93;
+    public static final int REM_INT = 0x94;
+    public static final int AND_INT = 0x95;
+    public static final int OR_INT = 0x96;
+    public static final int XOR_INT = 0x97;
+    public static final int SHL_INT = 0x98;
+    public static final int SHR_INT = 0x99;
+    public static final int USHR_INT = 0x9a;
+    public static final int ADD_LONG = 0x9b;
+    public static final int SUB_LONG = 0x9c;
+    public static final int MUL_LONG = 0x9d;
+    public static final int DIV_LONG = 0x9e;
+    public static final int REM_LONG = 0x9f;
+    public static final int AND_LONG = 0xa0;
+    public static final int OR_LONG = 0xa1;
+    public static final int XOR_LONG = 0xa2;
+    public static final int SHL_LONG = 0xa3;
+    public static final int SHR_LONG = 0xa4;
+    public static final int USHR_LONG = 0xa5;
+    public static final int ADD_FLOAT = 0xa6;
+    public static final int SUB_FLOAT = 0xa7;
+    public static final int MUL_FLOAT = 0xa8;
+    public static final int DIV_FLOAT = 0xa9;
+    public static final int REM_FLOAT = 0xaa;
+    public static final int ADD_DOUBLE = 0xab;
+    public static final int SUB_DOUBLE = 0xac;
+    public static final int MUL_DOUBLE = 0xad;
+    public static final int DIV_DOUBLE = 0xae;
+    public static final int REM_DOUBLE = 0xaf;
+    public static final int ADD_INT_2ADDR = 0xb0;
+    public static final int SUB_INT_2ADDR = 0xb1;
+    public static final int MUL_INT_2ADDR = 0xb2;
+    public static final int DIV_INT_2ADDR = 0xb3;
+    public static final int REM_INT_2ADDR = 0xb4;
+    public static final int AND_INT_2ADDR = 0xb5;
+    public static final int OR_INT_2ADDR = 0xb6;
+    public static final int XOR_INT_2ADDR = 0xb7;
+    public static final int SHL_INT_2ADDR = 0xb8;
+    public static final int SHR_INT_2ADDR = 0xb9;
+    public static final int USHR_INT_2ADDR = 0xba;
+    public static final int ADD_LONG_2ADDR = 0xbb;
+    public static final int SUB_LONG_2ADDR = 0xbc;
+    public static final int MUL_LONG_2ADDR = 0xbd;
+    public static final int DIV_LONG_2ADDR = 0xbe;
+    public static final int REM_LONG_2ADDR = 0xbf;
+    public static final int AND_LONG_2ADDR = 0xc0;
+    public static final int OR_LONG_2ADDR = 0xc1;
+    public static final int XOR_LONG_2ADDR = 0xc2;
+    public static final int SHL_LONG_2ADDR = 0xc3;
+    public static final int SHR_LONG_2ADDR = 0xc4;
+    public static final int USHR_LONG_2ADDR = 0xc5;
+    public static final int ADD_FLOAT_2ADDR = 0xc6;
+    public static final int SUB_FLOAT_2ADDR = 0xc7;
+    public static final int MUL_FLOAT_2ADDR = 0xc8;
+    public static final int DIV_FLOAT_2ADDR = 0xc9;
+    public static final int REM_FLOAT_2ADDR = 0xca;
+    public static final int ADD_DOUBLE_2ADDR = 0xcb;
+    public static final int SUB_DOUBLE_2ADDR = 0xcc;
+    public static final int MUL_DOUBLE_2ADDR = 0xcd;
+    public static final int DIV_DOUBLE_2ADDR = 0xce;
+    public static final int REM_DOUBLE_2ADDR = 0xcf;
+    public static final int ADD_INT_LIT16 = 0xd0;
+    public static final int RSUB_INT = 0xd1;
+    public static final int MUL_INT_LIT16 = 0xd2;
+    public static final int DIV_INT_LIT16 = 0xd3;
+    public static final int REM_INT_LIT16 = 0xd4;
+    public static final int AND_INT_LIT16 = 0xd5;
+    public static final int OR_INT_LIT16 = 0xd6;
+    public static final int XOR_INT_LIT16 = 0xd7;
+    public static final int ADD_INT_LIT8 = 0xd8;
+    public static final int RSUB_INT_LIT8 = 0xd9;
+    public static final int MUL_INT_LIT8 = 0xda;
+    public static final int DIV_INT_LIT8 = 0xdb;
+    public static final int REM_INT_LIT8 = 0xdc;
+    public static final int AND_INT_LIT8 = 0xdd;
+    public static final int OR_INT_LIT8 = 0xde;
+    public static final int XOR_INT_LIT8 = 0xdf;
+    public static final int SHL_INT_LIT8 = 0xe0;
+    public static final int SHR_INT_LIT8 = 0xe1;
+    public static final int USHR_INT_LIT8 = 0xe2;
+    public static final int UNUSED_E3 = 0xe3;
+    public static final int UNUSED_E4 = 0xe4;
+    public static final int UNUSED_E5 = 0xe5;
+    public static final int UNUSED_E6 = 0xe6;
+    public static final int UNUSED_E7 = 0xe7;
+    public static final int UNUSED_E8 = 0xe8;
+    public static final int UNUSED_E9 = 0xe9;
+    public static final int UNUSED_EA = 0xea;
+    public static final int UNUSED_EB = 0xeb;
+    public static final int UNUSED_EC = 0xec;
+    public static final int UNUSED_ED = 0xed;
+    public static final int UNUSED_EE = 0xee;
+    public static final int UNUSED_EF = 0xef;
+    public static final int UNUSED_F0 = 0xf0;
+    public static final int UNUSED_F1 = 0xf1;
+    public static final int UNUSED_F2 = 0xf2;
+    public static final int UNUSED_F3 = 0xf3;
+    public static final int UNUSED_F4 = 0xf4;
+    public static final int UNUSED_F5 = 0xf5;
+    public static final int UNUSED_F6 = 0xf6;
+    public static final int UNUSED_F7 = 0xf7;
+    public static final int UNUSED_F8 = 0xf8;
+    public static final int UNUSED_F9 = 0xf9;
+    public static final int UNUSED_FA = 0xfa;
+    public static final int UNUSED_FB = 0xfb;
+    public static final int UNUSED_FC = 0xfc;
+    public static final int UNUSED_FD = 0xfd;
+    public static final int UNUSED_FE = 0xfe;
+    public static final int UNUSED_FF = 0xff;
+    // END(opcodes)
+
+    /**
+     * This class is uninstantiable.
+     */
+    private DalvOps() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dop.java b/dx/src/com/android/dx/dex/code/Dop.java
new file mode 100644
index 0000000..6914fca
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dop.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * Representation of an opcode.
+ */
+public final class Dop {
+    /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value itself */
+    private final int opcode;
+
+    /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family */
+    private final int family;
+
+    /** non-null; the instruction format */
+    private final InsnFormat format;
+
+    /** whether this opcode uses a result register */
+    private final boolean hasResult;
+
+    /** non-null; the name */
+    private final String name;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode
+     * value itself
+     * @param family DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family
+     * @param format non-null; the instruction format
+     * @param hasResult whether the opcode has a result register; if so it
+     * is always the first register
+     * @param name non-null; the name
+     */
+    public Dop(int opcode, int family, InsnFormat format,
+               boolean hasResult, String name) {
+        if ((opcode < DalvOps.MIN_VALUE) || (opcode > DalvOps.MAX_VALUE)) {
+            throw new IllegalArgumentException("bogus opcode");
+        }
+
+        if ((family < DalvOps.MIN_VALUE) || (family > DalvOps.MAX_VALUE)) {
+            throw new IllegalArgumentException("bogus family");
+        }
+
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        this.opcode = opcode;
+        this.family = family;
+        this.format = format;
+        this.hasResult = hasResult;
+        this.name = name;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Gets the opcode value.
+     * 
+     * @return DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value
+     */
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the opcode family. The opcode family is the unmarked (no
+     * "/...") opcode that has equivalent semantics to this one.
+     * 
+     * @return DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family
+     */
+    public int getFamily() {
+        return family;
+    }
+
+    /**
+     * Gets the instruction format.
+     * 
+     * @return non-null; the instruction format
+     */
+    public InsnFormat getFormat() {
+        return format;
+    }
+
+    /**
+     * Returns whether this opcode uses a result register.
+     * 
+     * @return <code>true</code> iff this opcode uses a result register
+     */
+    public boolean hasResult() {
+        return hasResult;
+    }
+
+    /**
+     * Gets the opcode name.
+     * 
+     * @return non-null; the opcode name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the opcode for the opposite test of this instance. This is only
+     * valid for opcodes which are in fact tests.
+     * 
+     * @return non-null; the opposite test
+     */
+    public Dop getOppositeTest() {
+        switch (opcode) {
+            case DalvOps.IF_EQ:  return Dops.IF_NE;
+            case DalvOps.IF_NE:  return Dops.IF_EQ;
+            case DalvOps.IF_LT:  return Dops.IF_GE;
+            case DalvOps.IF_GE:  return Dops.IF_LT;
+            case DalvOps.IF_GT:  return Dops.IF_LE;
+            case DalvOps.IF_LE:  return Dops.IF_GT;
+            case DalvOps.IF_EQZ: return Dops.IF_NEZ;
+            case DalvOps.IF_NEZ: return Dops.IF_EQZ;
+            case DalvOps.IF_LTZ: return Dops.IF_GEZ;
+            case DalvOps.IF_GEZ: return Dops.IF_LTZ;
+            case DalvOps.IF_GTZ: return Dops.IF_LEZ;
+            case DalvOps.IF_LEZ: return Dops.IF_GTZ;
+        }
+
+        throw new IllegalArgumentException("bogus opcode: " + this);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java
new file mode 100644
index 0000000..8923c59
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.dex.code.form.Form10t;
+import com.android.dx.dex.code.form.Form10x;
+import com.android.dx.dex.code.form.Form11n;
+import com.android.dx.dex.code.form.Form11x;
+import com.android.dx.dex.code.form.Form12x;
+import com.android.dx.dex.code.form.Form20t;
+import com.android.dx.dex.code.form.Form21c;
+import com.android.dx.dex.code.form.Form21h;
+import com.android.dx.dex.code.form.Form21s;
+import com.android.dx.dex.code.form.Form21t;
+import com.android.dx.dex.code.form.Form22b;
+import com.android.dx.dex.code.form.Form22c;
+import com.android.dx.dex.code.form.Form22s;
+import com.android.dx.dex.code.form.Form22t;
+import com.android.dx.dex.code.form.Form22x;
+import com.android.dx.dex.code.form.Form23x;
+import com.android.dx.dex.code.form.Form30t;
+import com.android.dx.dex.code.form.Form31c;
+import com.android.dx.dex.code.form.Form31i;
+import com.android.dx.dex.code.form.Form31t;
+import com.android.dx.dex.code.form.Form32x;
+import com.android.dx.dex.code.form.Form35c;
+import com.android.dx.dex.code.form.Form3rc;
+import com.android.dx.dex.code.form.Form51l;
+import com.android.dx.dex.code.form.SpecialFormat;
+
+/**
+ * Standard instances of {@link Dop} and utility methods for getting
+ * them.
+ */
+public final class Dops {
+    /** non-null; array containing all the standard instances */
+    private static final Dop[] DOPS;
+
+    /**
+     * pseudo-opcode used for nonstandard formatted "instructions" 
+     * (which are mostly not actually instructions, though they do
+     * appear in instruction lists) 
+     */
+    public static final Dop SPECIAL_FORMAT =
+        new Dop(DalvOps.SPECIAL_FORMAT, DalvOps.SPECIAL_FORMAT,
+                SpecialFormat.THE_ONE, false, "<special>");
+
+    // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final Dop NOP =
+        new Dop(DalvOps.NOP, DalvOps.NOP,
+            Form10x.THE_ONE, false, "nop");
+
+    public static final Dop MOVE =
+        new Dop(DalvOps.MOVE, DalvOps.MOVE,
+            Form12x.THE_ONE, true, "move");
+
+    public static final Dop MOVE_FROM16 =
+        new Dop(DalvOps.MOVE_FROM16, DalvOps.MOVE,
+            Form22x.THE_ONE, true, "move/from16");
+
+    public static final Dop MOVE_16 =
+        new Dop(DalvOps.MOVE_16, DalvOps.MOVE,
+            Form32x.THE_ONE, true, "move/16");
+
+    public static final Dop MOVE_WIDE =
+        new Dop(DalvOps.MOVE_WIDE, DalvOps.MOVE_WIDE,
+            Form12x.THE_ONE, true, "move-wide");
+
+    public static final Dop MOVE_WIDE_FROM16 =
+        new Dop(DalvOps.MOVE_WIDE_FROM16, DalvOps.MOVE_WIDE,
+            Form22x.THE_ONE, true, "move-wide/from16");
+
+    public static final Dop MOVE_WIDE_16 =
+        new Dop(DalvOps.MOVE_WIDE_16, DalvOps.MOVE_WIDE,
+            Form32x.THE_ONE, true, "move-wide/16");
+
+    public static final Dop MOVE_OBJECT =
+        new Dop(DalvOps.MOVE_OBJECT, DalvOps.MOVE_OBJECT,
+            Form12x.THE_ONE, true, "move-object");
+
+    public static final Dop MOVE_OBJECT_FROM16 =
+        new Dop(DalvOps.MOVE_OBJECT_FROM16, DalvOps.MOVE_OBJECT,
+            Form22x.THE_ONE, true, "move-object/from16");
+
+    public static final Dop MOVE_OBJECT_16 =
+        new Dop(DalvOps.MOVE_OBJECT_16, DalvOps.MOVE_OBJECT,
+            Form32x.THE_ONE, true, "move-object/16");
+
+    public static final Dop MOVE_RESULT =
+        new Dop(DalvOps.MOVE_RESULT, DalvOps.MOVE_RESULT,
+            Form11x.THE_ONE, true, "move-result");
+
+    public static final Dop MOVE_RESULT_WIDE =
+        new Dop(DalvOps.MOVE_RESULT_WIDE, DalvOps.MOVE_RESULT_WIDE,
+            Form11x.THE_ONE, true, "move-result-wide");
+
+    public static final Dop MOVE_RESULT_OBJECT =
+        new Dop(DalvOps.MOVE_RESULT_OBJECT, DalvOps.MOVE_RESULT_OBJECT,
+            Form11x.THE_ONE, true, "move-result-object");
+
+    public static final Dop MOVE_EXCEPTION =
+        new Dop(DalvOps.MOVE_EXCEPTION, DalvOps.MOVE_EXCEPTION,
+            Form11x.THE_ONE, true, "move-exception");
+
+    public static final Dop RETURN_VOID =
+        new Dop(DalvOps.RETURN_VOID, DalvOps.RETURN_VOID,
+            Form10x.THE_ONE, false, "return-void");
+
+    public static final Dop RETURN =
+        new Dop(DalvOps.RETURN, DalvOps.RETURN,
+            Form11x.THE_ONE, false, "return");
+
+    public static final Dop RETURN_WIDE =
+        new Dop(DalvOps.RETURN_WIDE, DalvOps.RETURN_WIDE,
+            Form11x.THE_ONE, false, "return-wide");
+
+    public static final Dop RETURN_OBJECT =
+        new Dop(DalvOps.RETURN_OBJECT, DalvOps.RETURN_OBJECT,
+            Form11x.THE_ONE, false, "return-object");
+
+    public static final Dop CONST_4 =
+        new Dop(DalvOps.CONST_4, DalvOps.CONST,
+            Form11n.THE_ONE, true, "const/4");
+
+    public static final Dop CONST_16 =
+        new Dop(DalvOps.CONST_16, DalvOps.CONST,
+            Form21s.THE_ONE, true, "const/16");
+
+    public static final Dop CONST =
+        new Dop(DalvOps.CONST, DalvOps.CONST,
+            Form31i.THE_ONE, true, "const");
+
+    public static final Dop CONST_HIGH16 =
+        new Dop(DalvOps.CONST_HIGH16, DalvOps.CONST,
+            Form21h.THE_ONE, true, "const/high16");
+
+    public static final Dop CONST_WIDE_16 =
+        new Dop(DalvOps.CONST_WIDE_16, DalvOps.CONST_WIDE,
+            Form21s.THE_ONE, true, "const-wide/16");
+
+    public static final Dop CONST_WIDE_32 =
+        new Dop(DalvOps.CONST_WIDE_32, DalvOps.CONST_WIDE,
+            Form31i.THE_ONE, true, "const-wide/32");
+
+    public static final Dop CONST_WIDE =
+        new Dop(DalvOps.CONST_WIDE, DalvOps.CONST_WIDE,
+            Form51l.THE_ONE, true, "const-wide");
+
+    public static final Dop CONST_WIDE_HIGH16 =
+        new Dop(DalvOps.CONST_WIDE_HIGH16, DalvOps.CONST_WIDE,
+            Form21h.THE_ONE, true, "const-wide/high16");
+
+    public static final Dop CONST_STRING =
+        new Dop(DalvOps.CONST_STRING, DalvOps.CONST_STRING,
+            Form21c.THE_ONE, true, "const-string");
+
+    public static final Dop CONST_STRING_JUMBO =
+        new Dop(DalvOps.CONST_STRING_JUMBO, DalvOps.CONST_STRING,
+            Form31c.THE_ONE, true, "const-string/jumbo");
+
+    public static final Dop CONST_CLASS =
+        new Dop(DalvOps.CONST_CLASS, DalvOps.CONST_CLASS,
+            Form21c.THE_ONE, true, "const-class");
+
+    public static final Dop MONITOR_ENTER =
+        new Dop(DalvOps.MONITOR_ENTER, DalvOps.MONITOR_ENTER,
+            Form11x.THE_ONE, false, "monitor-enter");
+
+    public static final Dop MONITOR_EXIT =
+        new Dop(DalvOps.MONITOR_EXIT, DalvOps.MONITOR_EXIT,
+            Form11x.THE_ONE, false, "monitor-exit");
+
+    public static final Dop CHECK_CAST =
+        new Dop(DalvOps.CHECK_CAST, DalvOps.CHECK_CAST,
+            Form21c.THE_ONE, true, "check-cast");
+
+    public static final Dop INSTANCE_OF =
+        new Dop(DalvOps.INSTANCE_OF, DalvOps.INSTANCE_OF,
+            Form22c.THE_ONE, true, "instance-of");
+
+    public static final Dop ARRAY_LENGTH =
+        new Dop(DalvOps.ARRAY_LENGTH, DalvOps.ARRAY_LENGTH,
+            Form12x.THE_ONE, true, "array-length");
+
+    public static final Dop NEW_INSTANCE =
+        new Dop(DalvOps.NEW_INSTANCE, DalvOps.NEW_INSTANCE,
+            Form21c.THE_ONE, true, "new-instance");
+
+    public static final Dop NEW_ARRAY =
+        new Dop(DalvOps.NEW_ARRAY, DalvOps.NEW_ARRAY,
+            Form22c.THE_ONE, true, "new-array");
+
+    public static final Dop FILLED_NEW_ARRAY =
+        new Dop(DalvOps.FILLED_NEW_ARRAY, DalvOps.FILLED_NEW_ARRAY,
+            Form35c.THE_ONE, false, "filled-new-array");
+
+    public static final Dop FILLED_NEW_ARRAY_RANGE =
+        new Dop(DalvOps.FILLED_NEW_ARRAY_RANGE, DalvOps.FILLED_NEW_ARRAY,
+            Form3rc.THE_ONE, false, "filled-new-array/range");
+
+    public static final Dop FILL_ARRAY_DATA =
+        new Dop(DalvOps.FILL_ARRAY_DATA, DalvOps.FILL_ARRAY_DATA,
+            Form31t.THE_ONE, false, "fill-array-data");
+
+    public static final Dop THROW =
+        new Dop(DalvOps.THROW, DalvOps.THROW,
+            Form11x.THE_ONE, false, "throw");
+
+    public static final Dop GOTO =
+        new Dop(DalvOps.GOTO, DalvOps.GOTO,
+            Form10t.THE_ONE, false, "goto");
+
+    public static final Dop GOTO_16 =
+        new Dop(DalvOps.GOTO_16, DalvOps.GOTO,
+            Form20t.THE_ONE, false, "goto/16");
+
+    public static final Dop GOTO_32 =
+        new Dop(DalvOps.GOTO_32, DalvOps.GOTO,
+            Form30t.THE_ONE, false, "goto/32");
+
+    public static final Dop PACKED_SWITCH =
+        new Dop(DalvOps.PACKED_SWITCH, DalvOps.PACKED_SWITCH,
+            Form31t.THE_ONE, false, "packed-switch");
+
+    public static final Dop SPARSE_SWITCH =
+        new Dop(DalvOps.SPARSE_SWITCH, DalvOps.SPARSE_SWITCH,
+            Form31t.THE_ONE, false, "sparse-switch");
+
+    public static final Dop CMPL_FLOAT =
+        new Dop(DalvOps.CMPL_FLOAT, DalvOps.CMPL_FLOAT,
+            Form23x.THE_ONE, true, "cmpl-float");
+
+    public static final Dop CMPG_FLOAT =
+        new Dop(DalvOps.CMPG_FLOAT, DalvOps.CMPG_FLOAT,
+            Form23x.THE_ONE, true, "cmpg-float");
+
+    public static final Dop CMPL_DOUBLE =
+        new Dop(DalvOps.CMPL_DOUBLE, DalvOps.CMPL_DOUBLE,
+            Form23x.THE_ONE, true, "cmpl-double");
+
+    public static final Dop CMPG_DOUBLE =
+        new Dop(DalvOps.CMPG_DOUBLE, DalvOps.CMPG_DOUBLE,
+            Form23x.THE_ONE, true, "cmpg-double");
+
+    public static final Dop CMP_LONG =
+        new Dop(DalvOps.CMP_LONG, DalvOps.CMP_LONG,
+            Form23x.THE_ONE, true, "cmp-long");
+
+    public static final Dop IF_EQ =
+        new Dop(DalvOps.IF_EQ, DalvOps.IF_EQ,
+            Form22t.THE_ONE, false, "if-eq");
+
+    public static final Dop IF_NE =
+        new Dop(DalvOps.IF_NE, DalvOps.IF_NE,
+            Form22t.THE_ONE, false, "if-ne");
+
+    public static final Dop IF_LT =
+        new Dop(DalvOps.IF_LT, DalvOps.IF_LT,
+            Form22t.THE_ONE, false, "if-lt");
+
+    public static final Dop IF_GE =
+        new Dop(DalvOps.IF_GE, DalvOps.IF_GE,
+            Form22t.THE_ONE, false, "if-ge");
+
+    public static final Dop IF_GT =
+        new Dop(DalvOps.IF_GT, DalvOps.IF_GT,
+            Form22t.THE_ONE, false, "if-gt");
+
+    public static final Dop IF_LE =
+        new Dop(DalvOps.IF_LE, DalvOps.IF_LE,
+            Form22t.THE_ONE, false, "if-le");
+
+    public static final Dop IF_EQZ =
+        new Dop(DalvOps.IF_EQZ, DalvOps.IF_EQZ,
+            Form21t.THE_ONE, false, "if-eqz");
+
+    public static final Dop IF_NEZ =
+        new Dop(DalvOps.IF_NEZ, DalvOps.IF_NEZ,
+            Form21t.THE_ONE, false, "if-nez");
+
+    public static final Dop IF_LTZ =
+        new Dop(DalvOps.IF_LTZ, DalvOps.IF_LTZ,
+            Form21t.THE_ONE, false, "if-ltz");
+
+    public static final Dop IF_GEZ =
+        new Dop(DalvOps.IF_GEZ, DalvOps.IF_GEZ,
+            Form21t.THE_ONE, false, "if-gez");
+
+    public static final Dop IF_GTZ =
+        new Dop(DalvOps.IF_GTZ, DalvOps.IF_GTZ,
+            Form21t.THE_ONE, false, "if-gtz");
+
+    public static final Dop IF_LEZ =
+        new Dop(DalvOps.IF_LEZ, DalvOps.IF_LEZ,
+            Form21t.THE_ONE, false, "if-lez");
+
+    public static final Dop AGET =
+        new Dop(DalvOps.AGET, DalvOps.AGET,
+            Form23x.THE_ONE, true, "aget");
+
+    public static final Dop AGET_WIDE =
+        new Dop(DalvOps.AGET_WIDE, DalvOps.AGET_WIDE,
+            Form23x.THE_ONE, true, "aget-wide");
+
+    public static final Dop AGET_OBJECT =
+        new Dop(DalvOps.AGET_OBJECT, DalvOps.AGET_OBJECT,
+            Form23x.THE_ONE, true, "aget-object");
+
+    public static final Dop AGET_BOOLEAN =
+        new Dop(DalvOps.AGET_BOOLEAN, DalvOps.AGET_BOOLEAN,
+            Form23x.THE_ONE, true, "aget-boolean");
+
+    public static final Dop AGET_BYTE =
+        new Dop(DalvOps.AGET_BYTE, DalvOps.AGET_BYTE,
+            Form23x.THE_ONE, true, "aget-byte");
+
+    public static final Dop AGET_CHAR =
+        new Dop(DalvOps.AGET_CHAR, DalvOps.AGET_CHAR,
+            Form23x.THE_ONE, true, "aget-char");
+
+    public static final Dop AGET_SHORT =
+        new Dop(DalvOps.AGET_SHORT, DalvOps.AGET_SHORT,
+            Form23x.THE_ONE, true, "aget-short");
+
+    public static final Dop APUT =
+        new Dop(DalvOps.APUT, DalvOps.APUT,
+            Form23x.THE_ONE, false, "aput");
+
+    public static final Dop APUT_WIDE =
+        new Dop(DalvOps.APUT_WIDE, DalvOps.APUT_WIDE,
+            Form23x.THE_ONE, false, "aput-wide");
+
+    public static final Dop APUT_OBJECT =
+        new Dop(DalvOps.APUT_OBJECT, DalvOps.APUT_OBJECT,
+            Form23x.THE_ONE, false, "aput-object");
+
+    public static final Dop APUT_BOOLEAN =
+        new Dop(DalvOps.APUT_BOOLEAN, DalvOps.APUT_BOOLEAN,
+            Form23x.THE_ONE, false, "aput-boolean");
+
+    public static final Dop APUT_BYTE =
+        new Dop(DalvOps.APUT_BYTE, DalvOps.APUT_BYTE,
+            Form23x.THE_ONE, false, "aput-byte");
+
+    public static final Dop APUT_CHAR =
+        new Dop(DalvOps.APUT_CHAR, DalvOps.APUT_CHAR,
+            Form23x.THE_ONE, false, "aput-char");
+
+    public static final Dop APUT_SHORT =
+        new Dop(DalvOps.APUT_SHORT, DalvOps.APUT_SHORT,
+            Form23x.THE_ONE, false, "aput-short");
+
+    public static final Dop IGET =
+        new Dop(DalvOps.IGET, DalvOps.IGET,
+            Form22c.THE_ONE, true, "iget");
+
+    public static final Dop IGET_WIDE =
+        new Dop(DalvOps.IGET_WIDE, DalvOps.IGET_WIDE,
+            Form22c.THE_ONE, true, "iget-wide");
+
+    public static final Dop IGET_OBJECT =
+        new Dop(DalvOps.IGET_OBJECT, DalvOps.IGET_OBJECT,
+            Form22c.THE_ONE, true, "iget-object");
+
+    public static final Dop IGET_BOOLEAN =
+        new Dop(DalvOps.IGET_BOOLEAN, DalvOps.IGET_BOOLEAN,
+            Form22c.THE_ONE, true, "iget-boolean");
+
+    public static final Dop IGET_BYTE =
+        new Dop(DalvOps.IGET_BYTE, DalvOps.IGET_BYTE,
+            Form22c.THE_ONE, true, "iget-byte");
+
+    public static final Dop IGET_CHAR =
+        new Dop(DalvOps.IGET_CHAR, DalvOps.IGET_CHAR,
+            Form22c.THE_ONE, true, "iget-char");
+
+    public static final Dop IGET_SHORT =
+        new Dop(DalvOps.IGET_SHORT, DalvOps.IGET_SHORT,
+            Form22c.THE_ONE, true, "iget-short");
+
+    public static final Dop IPUT =
+        new Dop(DalvOps.IPUT, DalvOps.IPUT,
+            Form22c.THE_ONE, false, "iput");
+
+    public static final Dop IPUT_WIDE =
+        new Dop(DalvOps.IPUT_WIDE, DalvOps.IPUT_WIDE,
+            Form22c.THE_ONE, false, "iput-wide");
+
+    public static final Dop IPUT_OBJECT =
+        new Dop(DalvOps.IPUT_OBJECT, DalvOps.IPUT_OBJECT,
+            Form22c.THE_ONE, false, "iput-object");
+
+    public static final Dop IPUT_BOOLEAN =
+        new Dop(DalvOps.IPUT_BOOLEAN, DalvOps.IPUT_BOOLEAN,
+            Form22c.THE_ONE, false, "iput-boolean");
+
+    public static final Dop IPUT_BYTE =
+        new Dop(DalvOps.IPUT_BYTE, DalvOps.IPUT_BYTE,
+            Form22c.THE_ONE, false, "iput-byte");
+
+    public static final Dop IPUT_CHAR =
+        new Dop(DalvOps.IPUT_CHAR, DalvOps.IPUT_CHAR,
+            Form22c.THE_ONE, false, "iput-char");
+
+    public static final Dop IPUT_SHORT =
+        new Dop(DalvOps.IPUT_SHORT, DalvOps.IPUT_SHORT,
+            Form22c.THE_ONE, false, "iput-short");
+
+    public static final Dop SGET =
+        new Dop(DalvOps.SGET, DalvOps.SGET,
+            Form21c.THE_ONE, true, "sget");
+
+    public static final Dop SGET_WIDE =
+        new Dop(DalvOps.SGET_WIDE, DalvOps.SGET_WIDE,
+            Form21c.THE_ONE, true, "sget-wide");
+
+    public static final Dop SGET_OBJECT =
+        new Dop(DalvOps.SGET_OBJECT, DalvOps.SGET_OBJECT,
+            Form21c.THE_ONE, true, "sget-object");
+
+    public static final Dop SGET_BOOLEAN =
+        new Dop(DalvOps.SGET_BOOLEAN, DalvOps.SGET_BOOLEAN,
+            Form21c.THE_ONE, true, "sget-boolean");
+
+    public static final Dop SGET_BYTE =
+        new Dop(DalvOps.SGET_BYTE, DalvOps.SGET_BYTE,
+            Form21c.THE_ONE, true, "sget-byte");
+
+    public static final Dop SGET_CHAR =
+        new Dop(DalvOps.SGET_CHAR, DalvOps.SGET_CHAR,
+            Form21c.THE_ONE, true, "sget-char");
+
+    public static final Dop SGET_SHORT =
+        new Dop(DalvOps.SGET_SHORT, DalvOps.SGET_SHORT,
+            Form21c.THE_ONE, true, "sget-short");
+
+    public static final Dop SPUT =
+        new Dop(DalvOps.SPUT, DalvOps.SPUT,
+            Form21c.THE_ONE, false, "sput");
+
+    public static final Dop SPUT_WIDE =
+        new Dop(DalvOps.SPUT_WIDE, DalvOps.SPUT_WIDE,
+            Form21c.THE_ONE, false, "sput-wide");
+
+    public static final Dop SPUT_OBJECT =
+        new Dop(DalvOps.SPUT_OBJECT, DalvOps.SPUT_OBJECT,
+            Form21c.THE_ONE, false, "sput-object");
+
+    public static final Dop SPUT_BOOLEAN =
+        new Dop(DalvOps.SPUT_BOOLEAN, DalvOps.SPUT_BOOLEAN,
+            Form21c.THE_ONE, false, "sput-boolean");
+
+    public static final Dop SPUT_BYTE =
+        new Dop(DalvOps.SPUT_BYTE, DalvOps.SPUT_BYTE,
+            Form21c.THE_ONE, false, "sput-byte");
+
+    public static final Dop SPUT_CHAR =
+        new Dop(DalvOps.SPUT_CHAR, DalvOps.SPUT_CHAR,
+            Form21c.THE_ONE, false, "sput-char");
+
+    public static final Dop SPUT_SHORT =
+        new Dop(DalvOps.SPUT_SHORT, DalvOps.SPUT_SHORT,
+            Form21c.THE_ONE, false, "sput-short");
+
+    public static final Dop INVOKE_VIRTUAL =
+        new Dop(DalvOps.INVOKE_VIRTUAL, DalvOps.INVOKE_VIRTUAL,
+            Form35c.THE_ONE, false, "invoke-virtual");
+
+    public static final Dop INVOKE_SUPER =
+        new Dop(DalvOps.INVOKE_SUPER, DalvOps.INVOKE_SUPER,
+            Form35c.THE_ONE, false, "invoke-super");
+
+    public static final Dop INVOKE_DIRECT =
+        new Dop(DalvOps.INVOKE_DIRECT, DalvOps.INVOKE_DIRECT,
+            Form35c.THE_ONE, false, "invoke-direct");
+
+    public static final Dop INVOKE_STATIC =
+        new Dop(DalvOps.INVOKE_STATIC, DalvOps.INVOKE_STATIC,
+            Form35c.THE_ONE, false, "invoke-static");
+
+    public static final Dop INVOKE_INTERFACE =
+        new Dop(DalvOps.INVOKE_INTERFACE, DalvOps.INVOKE_INTERFACE,
+            Form35c.THE_ONE, false, "invoke-interface");
+
+    public static final Dop INVOKE_VIRTUAL_RANGE =
+        new Dop(DalvOps.INVOKE_VIRTUAL_RANGE, DalvOps.INVOKE_VIRTUAL,
+            Form3rc.THE_ONE, false, "invoke-virtual/range");
+
+    public static final Dop INVOKE_SUPER_RANGE =
+        new Dop(DalvOps.INVOKE_SUPER_RANGE, DalvOps.INVOKE_SUPER,
+            Form3rc.THE_ONE, false, "invoke-super/range");
+
+    public static final Dop INVOKE_DIRECT_RANGE =
+        new Dop(DalvOps.INVOKE_DIRECT_RANGE, DalvOps.INVOKE_DIRECT,
+            Form3rc.THE_ONE, false, "invoke-direct/range");
+
+    public static final Dop INVOKE_STATIC_RANGE =
+        new Dop(DalvOps.INVOKE_STATIC_RANGE, DalvOps.INVOKE_STATIC,
+            Form3rc.THE_ONE, false, "invoke-static/range");
+
+    public static final Dop INVOKE_INTERFACE_RANGE =
+        new Dop(DalvOps.INVOKE_INTERFACE_RANGE, DalvOps.INVOKE_INTERFACE,
+            Form3rc.THE_ONE, false, "invoke-interface/range");
+
+    public static final Dop NEG_INT =
+        new Dop(DalvOps.NEG_INT, DalvOps.NEG_INT,
+            Form12x.THE_ONE, true, "neg-int");
+
+    public static final Dop NOT_INT =
+        new Dop(DalvOps.NOT_INT, DalvOps.NOT_INT,
+            Form12x.THE_ONE, true, "not-int");
+
+    public static final Dop NEG_LONG =
+        new Dop(DalvOps.NEG_LONG, DalvOps.NEG_LONG,
+            Form12x.THE_ONE, true, "neg-long");
+
+    public static final Dop NOT_LONG =
+        new Dop(DalvOps.NOT_LONG, DalvOps.NOT_LONG,
+            Form12x.THE_ONE, true, "not-long");
+
+    public static final Dop NEG_FLOAT =
+        new Dop(DalvOps.NEG_FLOAT, DalvOps.NEG_FLOAT,
+            Form12x.THE_ONE, true, "neg-float");
+
+    public static final Dop NEG_DOUBLE =
+        new Dop(DalvOps.NEG_DOUBLE, DalvOps.NEG_DOUBLE,
+            Form12x.THE_ONE, true, "neg-double");
+
+    public static final Dop INT_TO_LONG =
+        new Dop(DalvOps.INT_TO_LONG, DalvOps.INT_TO_LONG,
+            Form12x.THE_ONE, true, "int-to-long");
+
+    public static final Dop INT_TO_FLOAT =
+        new Dop(DalvOps.INT_TO_FLOAT, DalvOps.INT_TO_FLOAT,
+            Form12x.THE_ONE, true, "int-to-float");
+
+    public static final Dop INT_TO_DOUBLE =
+        new Dop(DalvOps.INT_TO_DOUBLE, DalvOps.INT_TO_DOUBLE,
+            Form12x.THE_ONE, true, "int-to-double");
+
+    public static final Dop LONG_TO_INT =
+        new Dop(DalvOps.LONG_TO_INT, DalvOps.LONG_TO_INT,
+            Form12x.THE_ONE, true, "long-to-int");
+
+    public static final Dop LONG_TO_FLOAT =
+        new Dop(DalvOps.LONG_TO_FLOAT, DalvOps.LONG_TO_FLOAT,
+            Form12x.THE_ONE, true, "long-to-float");
+
+    public static final Dop LONG_TO_DOUBLE =
+        new Dop(DalvOps.LONG_TO_DOUBLE, DalvOps.LONG_TO_DOUBLE,
+            Form12x.THE_ONE, true, "long-to-double");
+
+    public static final Dop FLOAT_TO_INT =
+        new Dop(DalvOps.FLOAT_TO_INT, DalvOps.FLOAT_TO_INT,
+            Form12x.THE_ONE, true, "float-to-int");
+
+    public static final Dop FLOAT_TO_LONG =
+        new Dop(DalvOps.FLOAT_TO_LONG, DalvOps.FLOAT_TO_LONG,
+            Form12x.THE_ONE, true, "float-to-long");
+
+    public static final Dop FLOAT_TO_DOUBLE =
+        new Dop(DalvOps.FLOAT_TO_DOUBLE, DalvOps.FLOAT_TO_DOUBLE,
+            Form12x.THE_ONE, true, "float-to-double");
+
+    public static final Dop DOUBLE_TO_INT =
+        new Dop(DalvOps.DOUBLE_TO_INT, DalvOps.DOUBLE_TO_INT,
+            Form12x.THE_ONE, true, "double-to-int");
+
+    public static final Dop DOUBLE_TO_LONG =
+        new Dop(DalvOps.DOUBLE_TO_LONG, DalvOps.DOUBLE_TO_LONG,
+            Form12x.THE_ONE, true, "double-to-long");
+
+    public static final Dop DOUBLE_TO_FLOAT =
+        new Dop(DalvOps.DOUBLE_TO_FLOAT, DalvOps.DOUBLE_TO_FLOAT,
+            Form12x.THE_ONE, true, "double-to-float");
+
+    public static final Dop INT_TO_BYTE =
+        new Dop(DalvOps.INT_TO_BYTE, DalvOps.INT_TO_BYTE,
+            Form12x.THE_ONE, true, "int-to-byte");
+
+    public static final Dop INT_TO_CHAR =
+        new Dop(DalvOps.INT_TO_CHAR, DalvOps.INT_TO_CHAR,
+            Form12x.THE_ONE, true, "int-to-char");
+
+    public static final Dop INT_TO_SHORT =
+        new Dop(DalvOps.INT_TO_SHORT, DalvOps.INT_TO_SHORT,
+            Form12x.THE_ONE, true, "int-to-short");
+
+    public static final Dop ADD_INT =
+        new Dop(DalvOps.ADD_INT, DalvOps.ADD_INT,
+            Form23x.THE_ONE, true, "add-int");
+
+    public static final Dop SUB_INT =
+        new Dop(DalvOps.SUB_INT, DalvOps.SUB_INT,
+            Form23x.THE_ONE, true, "sub-int");
+
+    public static final Dop MUL_INT =
+        new Dop(DalvOps.MUL_INT, DalvOps.MUL_INT,
+            Form23x.THE_ONE, true, "mul-int");
+
+    public static final Dop DIV_INT =
+        new Dop(DalvOps.DIV_INT, DalvOps.DIV_INT,
+            Form23x.THE_ONE, true, "div-int");
+
+    public static final Dop REM_INT =
+        new Dop(DalvOps.REM_INT, DalvOps.REM_INT,
+            Form23x.THE_ONE, true, "rem-int");
+
+    public static final Dop AND_INT =
+        new Dop(DalvOps.AND_INT, DalvOps.AND_INT,
+            Form23x.THE_ONE, true, "and-int");
+
+    public static final Dop OR_INT =
+        new Dop(DalvOps.OR_INT, DalvOps.OR_INT,
+            Form23x.THE_ONE, true, "or-int");
+
+    public static final Dop XOR_INT =
+        new Dop(DalvOps.XOR_INT, DalvOps.XOR_INT,
+            Form23x.THE_ONE, true, "xor-int");
+
+    public static final Dop SHL_INT =
+        new Dop(DalvOps.SHL_INT, DalvOps.SHL_INT,
+            Form23x.THE_ONE, true, "shl-int");
+
+    public static final Dop SHR_INT =
+        new Dop(DalvOps.SHR_INT, DalvOps.SHR_INT,
+            Form23x.THE_ONE, true, "shr-int");
+
+    public static final Dop USHR_INT =
+        new Dop(DalvOps.USHR_INT, DalvOps.USHR_INT,
+            Form23x.THE_ONE, true, "ushr-int");
+
+    public static final Dop ADD_LONG =
+        new Dop(DalvOps.ADD_LONG, DalvOps.ADD_LONG,
+            Form23x.THE_ONE, true, "add-long");
+
+    public static final Dop SUB_LONG =
+        new Dop(DalvOps.SUB_LONG, DalvOps.SUB_LONG,
+            Form23x.THE_ONE, true, "sub-long");
+
+    public static final Dop MUL_LONG =
+        new Dop(DalvOps.MUL_LONG, DalvOps.MUL_LONG,
+            Form23x.THE_ONE, true, "mul-long");
+
+    public static final Dop DIV_LONG =
+        new Dop(DalvOps.DIV_LONG, DalvOps.DIV_LONG,
+            Form23x.THE_ONE, true, "div-long");
+
+    public static final Dop REM_LONG =
+        new Dop(DalvOps.REM_LONG, DalvOps.REM_LONG,
+            Form23x.THE_ONE, true, "rem-long");
+
+    public static final Dop AND_LONG =
+        new Dop(DalvOps.AND_LONG, DalvOps.AND_LONG,
+            Form23x.THE_ONE, true, "and-long");
+
+    public static final Dop OR_LONG =
+        new Dop(DalvOps.OR_LONG, DalvOps.OR_LONG,
+            Form23x.THE_ONE, true, "or-long");
+
+    public static final Dop XOR_LONG =
+        new Dop(DalvOps.XOR_LONG, DalvOps.XOR_LONG,
+            Form23x.THE_ONE, true, "xor-long");
+
+    public static final Dop SHL_LONG =
+        new Dop(DalvOps.SHL_LONG, DalvOps.SHL_LONG,
+            Form23x.THE_ONE, true, "shl-long");
+
+    public static final Dop SHR_LONG =
+        new Dop(DalvOps.SHR_LONG, DalvOps.SHR_LONG,
+            Form23x.THE_ONE, true, "shr-long");
+
+    public static final Dop USHR_LONG =
+        new Dop(DalvOps.USHR_LONG, DalvOps.USHR_LONG,
+            Form23x.THE_ONE, true, "ushr-long");
+
+    public static final Dop ADD_FLOAT =
+        new Dop(DalvOps.ADD_FLOAT, DalvOps.ADD_FLOAT,
+            Form23x.THE_ONE, true, "add-float");
+
+    public static final Dop SUB_FLOAT =
+        new Dop(DalvOps.SUB_FLOAT, DalvOps.SUB_FLOAT,
+            Form23x.THE_ONE, true, "sub-float");
+
+    public static final Dop MUL_FLOAT =
+        new Dop(DalvOps.MUL_FLOAT, DalvOps.MUL_FLOAT,
+            Form23x.THE_ONE, true, "mul-float");
+
+    public static final Dop DIV_FLOAT =
+        new Dop(DalvOps.DIV_FLOAT, DalvOps.DIV_FLOAT,
+            Form23x.THE_ONE, true, "div-float");
+
+    public static final Dop REM_FLOAT =
+        new Dop(DalvOps.REM_FLOAT, DalvOps.REM_FLOAT,
+            Form23x.THE_ONE, true, "rem-float");
+
+    public static final Dop ADD_DOUBLE =
+        new Dop(DalvOps.ADD_DOUBLE, DalvOps.ADD_DOUBLE,
+            Form23x.THE_ONE, true, "add-double");
+
+    public static final Dop SUB_DOUBLE =
+        new Dop(DalvOps.SUB_DOUBLE, DalvOps.SUB_DOUBLE,
+            Form23x.THE_ONE, true, "sub-double");
+
+    public static final Dop MUL_DOUBLE =
+        new Dop(DalvOps.MUL_DOUBLE, DalvOps.MUL_DOUBLE,
+            Form23x.THE_ONE, true, "mul-double");
+
+    public static final Dop DIV_DOUBLE =
+        new Dop(DalvOps.DIV_DOUBLE, DalvOps.DIV_DOUBLE,
+            Form23x.THE_ONE, true, "div-double");
+
+    public static final Dop REM_DOUBLE =
+        new Dop(DalvOps.REM_DOUBLE, DalvOps.REM_DOUBLE,
+            Form23x.THE_ONE, true, "rem-double");
+
+    public static final Dop ADD_INT_2ADDR =
+        new Dop(DalvOps.ADD_INT_2ADDR, DalvOps.ADD_INT,
+            Form12x.THE_ONE, true, "add-int/2addr");
+
+    public static final Dop SUB_INT_2ADDR =
+        new Dop(DalvOps.SUB_INT_2ADDR, DalvOps.SUB_INT,
+            Form12x.THE_ONE, true, "sub-int/2addr");
+
+    public static final Dop MUL_INT_2ADDR =
+        new Dop(DalvOps.MUL_INT_2ADDR, DalvOps.MUL_INT,
+            Form12x.THE_ONE, true, "mul-int/2addr");
+
+    public static final Dop DIV_INT_2ADDR =
+        new Dop(DalvOps.DIV_INT_2ADDR, DalvOps.DIV_INT,
+            Form12x.THE_ONE, true, "div-int/2addr");
+
+    public static final Dop REM_INT_2ADDR =
+        new Dop(DalvOps.REM_INT_2ADDR, DalvOps.REM_INT,
+            Form12x.THE_ONE, true, "rem-int/2addr");
+
+    public static final Dop AND_INT_2ADDR =
+        new Dop(DalvOps.AND_INT_2ADDR, DalvOps.AND_INT,
+            Form12x.THE_ONE, true, "and-int/2addr");
+
+    public static final Dop OR_INT_2ADDR =
+        new Dop(DalvOps.OR_INT_2ADDR, DalvOps.OR_INT,
+            Form12x.THE_ONE, true, "or-int/2addr");
+
+    public static final Dop XOR_INT_2ADDR =
+        new Dop(DalvOps.XOR_INT_2ADDR, DalvOps.XOR_INT,
+            Form12x.THE_ONE, true, "xor-int/2addr");
+
+    public static final Dop SHL_INT_2ADDR =
+        new Dop(DalvOps.SHL_INT_2ADDR, DalvOps.SHL_INT,
+            Form12x.THE_ONE, true, "shl-int/2addr");
+
+    public static final Dop SHR_INT_2ADDR =
+        new Dop(DalvOps.SHR_INT_2ADDR, DalvOps.SHR_INT,
+            Form12x.THE_ONE, true, "shr-int/2addr");
+
+    public static final Dop USHR_INT_2ADDR =
+        new Dop(DalvOps.USHR_INT_2ADDR, DalvOps.USHR_INT,
+            Form12x.THE_ONE, true, "ushr-int/2addr");
+
+    public static final Dop ADD_LONG_2ADDR =
+        new Dop(DalvOps.ADD_LONG_2ADDR, DalvOps.ADD_LONG,
+            Form12x.THE_ONE, true, "add-long/2addr");
+
+    public static final Dop SUB_LONG_2ADDR =
+        new Dop(DalvOps.SUB_LONG_2ADDR, DalvOps.SUB_LONG,
+            Form12x.THE_ONE, true, "sub-long/2addr");
+
+    public static final Dop MUL_LONG_2ADDR =
+        new Dop(DalvOps.MUL_LONG_2ADDR, DalvOps.MUL_LONG,
+            Form12x.THE_ONE, true, "mul-long/2addr");
+
+    public static final Dop DIV_LONG_2ADDR =
+        new Dop(DalvOps.DIV_LONG_2ADDR, DalvOps.DIV_LONG,
+            Form12x.THE_ONE, true, "div-long/2addr");
+
+    public static final Dop REM_LONG_2ADDR =
+        new Dop(DalvOps.REM_LONG_2ADDR, DalvOps.REM_LONG,
+            Form12x.THE_ONE, true, "rem-long/2addr");
+
+    public static final Dop AND_LONG_2ADDR =
+        new Dop(DalvOps.AND_LONG_2ADDR, DalvOps.AND_LONG,
+            Form12x.THE_ONE, true, "and-long/2addr");
+
+    public static final Dop OR_LONG_2ADDR =
+        new Dop(DalvOps.OR_LONG_2ADDR, DalvOps.OR_LONG,
+            Form12x.THE_ONE, true, "or-long/2addr");
+
+    public static final Dop XOR_LONG_2ADDR =
+        new Dop(DalvOps.XOR_LONG_2ADDR, DalvOps.XOR_LONG,
+            Form12x.THE_ONE, true, "xor-long/2addr");
+
+    public static final Dop SHL_LONG_2ADDR =
+        new Dop(DalvOps.SHL_LONG_2ADDR, DalvOps.SHL_LONG,
+            Form12x.THE_ONE, true, "shl-long/2addr");
+
+    public static final Dop SHR_LONG_2ADDR =
+        new Dop(DalvOps.SHR_LONG_2ADDR, DalvOps.SHR_LONG,
+            Form12x.THE_ONE, true, "shr-long/2addr");
+
+    public static final Dop USHR_LONG_2ADDR =
+        new Dop(DalvOps.USHR_LONG_2ADDR, DalvOps.USHR_LONG,
+            Form12x.THE_ONE, true, "ushr-long/2addr");
+
+    public static final Dop ADD_FLOAT_2ADDR =
+        new Dop(DalvOps.ADD_FLOAT_2ADDR, DalvOps.ADD_FLOAT,
+            Form12x.THE_ONE, true, "add-float/2addr");
+
+    public static final Dop SUB_FLOAT_2ADDR =
+        new Dop(DalvOps.SUB_FLOAT_2ADDR, DalvOps.SUB_FLOAT,
+            Form12x.THE_ONE, true, "sub-float/2addr");
+
+    public static final Dop MUL_FLOAT_2ADDR =
+        new Dop(DalvOps.MUL_FLOAT_2ADDR, DalvOps.MUL_FLOAT,
+            Form12x.THE_ONE, true, "mul-float/2addr");
+
+    public static final Dop DIV_FLOAT_2ADDR =
+        new Dop(DalvOps.DIV_FLOAT_2ADDR, DalvOps.DIV_FLOAT,
+            Form12x.THE_ONE, true, "div-float/2addr");
+
+    public static final Dop REM_FLOAT_2ADDR =
+        new Dop(DalvOps.REM_FLOAT_2ADDR, DalvOps.REM_FLOAT,
+            Form12x.THE_ONE, true, "rem-float/2addr");
+
+    public static final Dop ADD_DOUBLE_2ADDR =
+        new Dop(DalvOps.ADD_DOUBLE_2ADDR, DalvOps.ADD_DOUBLE,
+            Form12x.THE_ONE, true, "add-double/2addr");
+
+    public static final Dop SUB_DOUBLE_2ADDR =
+        new Dop(DalvOps.SUB_DOUBLE_2ADDR, DalvOps.SUB_DOUBLE,
+            Form12x.THE_ONE, true, "sub-double/2addr");
+
+    public static final Dop MUL_DOUBLE_2ADDR =
+        new Dop(DalvOps.MUL_DOUBLE_2ADDR, DalvOps.MUL_DOUBLE,
+            Form12x.THE_ONE, true, "mul-double/2addr");
+
+    public static final Dop DIV_DOUBLE_2ADDR =
+        new Dop(DalvOps.DIV_DOUBLE_2ADDR, DalvOps.DIV_DOUBLE,
+            Form12x.THE_ONE, true, "div-double/2addr");
+
+    public static final Dop REM_DOUBLE_2ADDR =
+        new Dop(DalvOps.REM_DOUBLE_2ADDR, DalvOps.REM_DOUBLE,
+            Form12x.THE_ONE, true, "rem-double/2addr");
+
+    public static final Dop ADD_INT_LIT16 =
+        new Dop(DalvOps.ADD_INT_LIT16, DalvOps.ADD_INT,
+            Form22s.THE_ONE, true, "add-int/lit16");
+
+    public static final Dop RSUB_INT =
+        new Dop(DalvOps.RSUB_INT, DalvOps.RSUB_INT,
+            Form22s.THE_ONE, true, "rsub-int");
+
+    public static final Dop MUL_INT_LIT16 =
+        new Dop(DalvOps.MUL_INT_LIT16, DalvOps.MUL_INT,
+            Form22s.THE_ONE, true, "mul-int/lit16");
+
+    public static final Dop DIV_INT_LIT16 =
+        new Dop(DalvOps.DIV_INT_LIT16, DalvOps.DIV_INT,
+            Form22s.THE_ONE, true, "div-int/lit16");
+
+    public static final Dop REM_INT_LIT16 =
+        new Dop(DalvOps.REM_INT_LIT16, DalvOps.REM_INT,
+            Form22s.THE_ONE, true, "rem-int/lit16");
+
+    public static final Dop AND_INT_LIT16 =
+        new Dop(DalvOps.AND_INT_LIT16, DalvOps.AND_INT,
+            Form22s.THE_ONE, true, "and-int/lit16");
+
+    public static final Dop OR_INT_LIT16 =
+        new Dop(DalvOps.OR_INT_LIT16, DalvOps.OR_INT,
+            Form22s.THE_ONE, true, "or-int/lit16");
+
+    public static final Dop XOR_INT_LIT16 =
+        new Dop(DalvOps.XOR_INT_LIT16, DalvOps.XOR_INT,
+            Form22s.THE_ONE, true, "xor-int/lit16");
+
+    public static final Dop ADD_INT_LIT8 =
+        new Dop(DalvOps.ADD_INT_LIT8, DalvOps.ADD_INT,
+            Form22b.THE_ONE, true, "add-int/lit8");
+
+    public static final Dop RSUB_INT_LIT8 =
+        new Dop(DalvOps.RSUB_INT_LIT8, DalvOps.RSUB_INT,
+            Form22b.THE_ONE, true, "rsub-int/lit8");
+
+    public static final Dop MUL_INT_LIT8 =
+        new Dop(DalvOps.MUL_INT_LIT8, DalvOps.MUL_INT,
+            Form22b.THE_ONE, true, "mul-int/lit8");
+
+    public static final Dop DIV_INT_LIT8 =
+        new Dop(DalvOps.DIV_INT_LIT8, DalvOps.DIV_INT,
+            Form22b.THE_ONE, true, "div-int/lit8");
+
+    public static final Dop REM_INT_LIT8 =
+        new Dop(DalvOps.REM_INT_LIT8, DalvOps.REM_INT,
+            Form22b.THE_ONE, true, "rem-int/lit8");
+
+    public static final Dop AND_INT_LIT8 =
+        new Dop(DalvOps.AND_INT_LIT8, DalvOps.AND_INT,
+            Form22b.THE_ONE, true, "and-int/lit8");
+
+    public static final Dop OR_INT_LIT8 =
+        new Dop(DalvOps.OR_INT_LIT8, DalvOps.OR_INT,
+            Form22b.THE_ONE, true, "or-int/lit8");
+
+    public static final Dop XOR_INT_LIT8 =
+        new Dop(DalvOps.XOR_INT_LIT8, DalvOps.XOR_INT,
+            Form22b.THE_ONE, true, "xor-int/lit8");
+
+    public static final Dop SHL_INT_LIT8 =
+        new Dop(DalvOps.SHL_INT_LIT8, DalvOps.SHL_INT,
+            Form22b.THE_ONE, true, "shl-int/lit8");
+
+    public static final Dop SHR_INT_LIT8 =
+        new Dop(DalvOps.SHR_INT_LIT8, DalvOps.SHR_INT,
+            Form22b.THE_ONE, true, "shr-int/lit8");
+
+    public static final Dop USHR_INT_LIT8 =
+        new Dop(DalvOps.USHR_INT_LIT8, DalvOps.USHR_INT,
+            Form22b.THE_ONE, true, "ushr-int/lit8");
+
+    // END(dops)
+
+    // Static initialization.
+    static {
+        DOPS = new Dop[DalvOps.MAX_VALUE - DalvOps.MIN_VALUE + 1];
+
+        set(SPECIAL_FORMAT);
+
+        // BEGIN(dops-init); GENERATED AUTOMATICALLY BY opcode-gen
+        set(NOP);
+        set(MOVE);
+        set(MOVE_FROM16);
+        set(MOVE_16);
+        set(MOVE_WIDE);
+        set(MOVE_WIDE_FROM16);
+        set(MOVE_WIDE_16);
+        set(MOVE_OBJECT);
+        set(MOVE_OBJECT_FROM16);
+        set(MOVE_OBJECT_16);
+        set(MOVE_RESULT);
+        set(MOVE_RESULT_WIDE);
+        set(MOVE_RESULT_OBJECT);
+        set(MOVE_EXCEPTION);
+        set(RETURN_VOID);
+        set(RETURN);
+        set(RETURN_WIDE);
+        set(RETURN_OBJECT);
+        set(CONST_4);
+        set(CONST_16);
+        set(CONST);
+        set(CONST_HIGH16);
+        set(CONST_WIDE_16);
+        set(CONST_WIDE_32);
+        set(CONST_WIDE);
+        set(CONST_WIDE_HIGH16);
+        set(CONST_STRING);
+        set(CONST_STRING_JUMBO);
+        set(CONST_CLASS);
+        set(MONITOR_ENTER);
+        set(MONITOR_EXIT);
+        set(CHECK_CAST);
+        set(INSTANCE_OF);
+        set(ARRAY_LENGTH);
+        set(NEW_INSTANCE);
+        set(NEW_ARRAY);
+        set(FILLED_NEW_ARRAY);
+        set(FILLED_NEW_ARRAY_RANGE);
+        set(FILL_ARRAY_DATA);
+        set(THROW);
+        set(GOTO);
+        set(GOTO_16);
+        set(GOTO_32);
+        set(PACKED_SWITCH);
+        set(SPARSE_SWITCH);
+        set(CMPL_FLOAT);
+        set(CMPG_FLOAT);
+        set(CMPL_DOUBLE);
+        set(CMPG_DOUBLE);
+        set(CMP_LONG);
+        set(IF_EQ);
+        set(IF_NE);
+        set(IF_LT);
+        set(IF_GE);
+        set(IF_GT);
+        set(IF_LE);
+        set(IF_EQZ);
+        set(IF_NEZ);
+        set(IF_LTZ);
+        set(IF_GEZ);
+        set(IF_GTZ);
+        set(IF_LEZ);
+        set(AGET);
+        set(AGET_WIDE);
+        set(AGET_OBJECT);
+        set(AGET_BOOLEAN);
+        set(AGET_BYTE);
+        set(AGET_CHAR);
+        set(AGET_SHORT);
+        set(APUT);
+        set(APUT_WIDE);
+        set(APUT_OBJECT);
+        set(APUT_BOOLEAN);
+        set(APUT_BYTE);
+        set(APUT_CHAR);
+        set(APUT_SHORT);
+        set(IGET);
+        set(IGET_WIDE);
+        set(IGET_OBJECT);
+        set(IGET_BOOLEAN);
+        set(IGET_BYTE);
+        set(IGET_CHAR);
+        set(IGET_SHORT);
+        set(IPUT);
+        set(IPUT_WIDE);
+        set(IPUT_OBJECT);
+        set(IPUT_BOOLEAN);
+        set(IPUT_BYTE);
+        set(IPUT_CHAR);
+        set(IPUT_SHORT);
+        set(SGET);
+        set(SGET_WIDE);
+        set(SGET_OBJECT);
+        set(SGET_BOOLEAN);
+        set(SGET_BYTE);
+        set(SGET_CHAR);
+        set(SGET_SHORT);
+        set(SPUT);
+        set(SPUT_WIDE);
+        set(SPUT_OBJECT);
+        set(SPUT_BOOLEAN);
+        set(SPUT_BYTE);
+        set(SPUT_CHAR);
+        set(SPUT_SHORT);
+        set(INVOKE_VIRTUAL);
+        set(INVOKE_SUPER);
+        set(INVOKE_DIRECT);
+        set(INVOKE_STATIC);
+        set(INVOKE_INTERFACE);
+        set(INVOKE_VIRTUAL_RANGE);
+        set(INVOKE_SUPER_RANGE);
+        set(INVOKE_DIRECT_RANGE);
+        set(INVOKE_STATIC_RANGE);
+        set(INVOKE_INTERFACE_RANGE);
+        set(NEG_INT);
+        set(NOT_INT);
+        set(NEG_LONG);
+        set(NOT_LONG);
+        set(NEG_FLOAT);
+        set(NEG_DOUBLE);
+        set(INT_TO_LONG);
+        set(INT_TO_FLOAT);
+        set(INT_TO_DOUBLE);
+        set(LONG_TO_INT);
+        set(LONG_TO_FLOAT);
+        set(LONG_TO_DOUBLE);
+        set(FLOAT_TO_INT);
+        set(FLOAT_TO_LONG);
+        set(FLOAT_TO_DOUBLE);
+        set(DOUBLE_TO_INT);
+        set(DOUBLE_TO_LONG);
+        set(DOUBLE_TO_FLOAT);
+        set(INT_TO_BYTE);
+        set(INT_TO_CHAR);
+        set(INT_TO_SHORT);
+        set(ADD_INT);
+        set(SUB_INT);
+        set(MUL_INT);
+        set(DIV_INT);
+        set(REM_INT);
+        set(AND_INT);
+        set(OR_INT);
+        set(XOR_INT);
+        set(SHL_INT);
+        set(SHR_INT);
+        set(USHR_INT);
+        set(ADD_LONG);
+        set(SUB_LONG);
+        set(MUL_LONG);
+        set(DIV_LONG);
+        set(REM_LONG);
+        set(AND_LONG);
+        set(OR_LONG);
+        set(XOR_LONG);
+        set(SHL_LONG);
+        set(SHR_LONG);
+        set(USHR_LONG);
+        set(ADD_FLOAT);
+        set(SUB_FLOAT);
+        set(MUL_FLOAT);
+        set(DIV_FLOAT);
+        set(REM_FLOAT);
+        set(ADD_DOUBLE);
+        set(SUB_DOUBLE);
+        set(MUL_DOUBLE);
+        set(DIV_DOUBLE);
+        set(REM_DOUBLE);
+        set(ADD_INT_2ADDR);
+        set(SUB_INT_2ADDR);
+        set(MUL_INT_2ADDR);
+        set(DIV_INT_2ADDR);
+        set(REM_INT_2ADDR);
+        set(AND_INT_2ADDR);
+        set(OR_INT_2ADDR);
+        set(XOR_INT_2ADDR);
+        set(SHL_INT_2ADDR);
+        set(SHR_INT_2ADDR);
+        set(USHR_INT_2ADDR);
+        set(ADD_LONG_2ADDR);
+        set(SUB_LONG_2ADDR);
+        set(MUL_LONG_2ADDR);
+        set(DIV_LONG_2ADDR);
+        set(REM_LONG_2ADDR);
+        set(AND_LONG_2ADDR);
+        set(OR_LONG_2ADDR);
+        set(XOR_LONG_2ADDR);
+        set(SHL_LONG_2ADDR);
+        set(SHR_LONG_2ADDR);
+        set(USHR_LONG_2ADDR);
+        set(ADD_FLOAT_2ADDR);
+        set(SUB_FLOAT_2ADDR);
+        set(MUL_FLOAT_2ADDR);
+        set(DIV_FLOAT_2ADDR);
+        set(REM_FLOAT_2ADDR);
+        set(ADD_DOUBLE_2ADDR);
+        set(SUB_DOUBLE_2ADDR);
+        set(MUL_DOUBLE_2ADDR);
+        set(DIV_DOUBLE_2ADDR);
+        set(REM_DOUBLE_2ADDR);
+        set(ADD_INT_LIT16);
+        set(RSUB_INT);
+        set(MUL_INT_LIT16);
+        set(DIV_INT_LIT16);
+        set(REM_INT_LIT16);
+        set(AND_INT_LIT16);
+        set(OR_INT_LIT16);
+        set(XOR_INT_LIT16);
+        set(ADD_INT_LIT8);
+        set(RSUB_INT_LIT8);
+        set(MUL_INT_LIT8);
+        set(DIV_INT_LIT8);
+        set(REM_INT_LIT8);
+        set(AND_INT_LIT8);
+        set(OR_INT_LIT8);
+        set(XOR_INT_LIT8);
+        set(SHL_INT_LIT8);
+        set(SHR_INT_LIT8);
+        set(USHR_INT_LIT8);
+        // END(dops-init)
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Dops() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the {@link Dop} for the given opcode value.
+     * 
+     * @param opcode DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value
+     * @return non-null; the associated opcode instance
+     */
+    public static Dop get(int opcode) {
+        int idx = opcode - DalvOps.MIN_VALUE;
+
+        try {
+            Dop result = DOPS[idx];
+            if (result != null) {
+                return result;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Fall through.
+        }
+
+        throw new IllegalArgumentException("bogus opcode");
+    }
+
+    /**
+     * Gets the {@link Dop} with the given family/format combination, if
+     * any.
+     * 
+     * @param family DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family
+     * @param format non-null; the opcode's instruction format
+     * @return null-ok; the corresponding opcode, or <code>null</code> if
+     * there is none
+     */
+    public static Dop getOrNull(int family, InsnFormat format) {
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        int len = DOPS.length;
+
+        // TODO: Linear search is bad.
+        for (int i = 0; i < len; i++) {
+            Dop dop = DOPS[i];
+            if ((dop != null) &&
+                (dop.getFamily() == family) &&
+                (dop.getFormat() == format)) {
+                return dop;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Puts the given opcode into the table of all ops.
+     * 
+     * @param opcode non-null; the opcode
+     */
+    private static void set(Dop opcode) {
+        int idx = opcode.getOpcode() - DalvOps.MIN_VALUE;
+        DOPS[idx] = opcode;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/FixedSizeInsn.java b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
new file mode 100644
index 0000000..63c9d24
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for instructions which are of a fixed code size and
+ * which use {@link InsnFormat} methods to write themselves. This
+ * includes most &mdash; but not all &mdash; instructions.
+ */
+public abstract class FixedSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * <p><b>Note:</b> In the unlikely event that an instruction takes
+     * absolutely no registers (e.g., a <code>nop</code> or a
+     * no-argument no-result * static method call), then the given
+     * register list may be passed as {@link
+     * RegisterSpecList#EMPTY}.</p>
+     * 
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position non-null; source position
+     * @param registers non-null; register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     */
+    public FixedSizeInsn(Dop opcode, SourcePosition position,
+                         RegisterSpecList registers) {
+        super(opcode, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int codeSize() {
+        return getOpcode().getFormat().codeSize();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(AnnotatedOutput out) {
+        getOpcode().getFormat().writeTo(out, this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final String listingString0(boolean noteIndices) {
+        return getOpcode().getFormat().listingString(this, noteIndices);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
new file mode 100644
index 0000000..458bc89
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Combination instruction which turns into a variable number of
+ * <code>move*</code> instructions to move a set of registers into
+ * registers starting at <code>0</code> sequentially. This is used
+ * in translating an instruction whose register requirements cannot
+ * be met using a straightforward choice of a single opcode.
+ */
+public final class HighRegisterPrefix extends VariableSizeInsn {
+    /** null-ok; cached instructions, if constructed */
+    private SimpleInsn[] insns;
+    
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param registers non-null; source registers
+     */
+    public HighRegisterPrefix(SourcePosition position,
+                              RegisterSpecList registers) {
+        super(position, registers);
+
+        if (registers.size() == 0) {
+            throw new IllegalArgumentException("registers.size() == 0");
+        }
+
+        insns = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        int result = 0;
+
+        calculateInsnsIfNecessary();
+
+        for (SimpleInsn insn : insns) {
+            result += insn.codeSize();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        calculateInsnsIfNecessary();
+
+        for (SimpleInsn insn : insns) {
+            insn.writeTo(out);
+        }
+    }
+
+    /**
+     * Helper for {@link #codeSize} and {@link #writeTo} which sets up
+     * {@link #insns} if not already done.
+     */
+    private void calculateInsnsIfNecessary() {
+        if (insns != null) {
+            return;
+        }
+        
+        RegisterSpecList registers = getRegisters();
+        int sz = registers.size();
+
+        insns = new SimpleInsn[sz];
+        
+        for (int i = 0, outAt = 0; i < sz; i++) {
+            RegisterSpec src = registers.get(i);
+            insns[i] = moveInsnFor(src, outAt);
+            outAt += src.getCategory();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new HighRegisterPrefix(getPosition(), registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        RegisterSpecList registers = getRegisters();
+        int sz = registers.size();
+        StringBuffer sb = new StringBuffer(100);
+
+        for (int i = 0, outAt = 0; i < sz; i++) {
+            RegisterSpec src = registers.get(i);
+            SimpleInsn insn = moveInsnFor(src, outAt);
+
+            if (i != 0) {
+                sb.append('\n');
+            }
+            
+            sb.append(insn.listingString0(noteIndices));
+
+            outAt += src.getCategory();
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the proper move instruction for the given source spec
+     * and destination index.
+     *
+     * @param src non-null; the source register spec
+     * @param destIndex &gt;= 0; the destination register index
+     * @return non-null; the appropriate move instruction
+     */
+    private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) {
+        return DalvInsn.makeMove(SourcePosition.NO_INFO,
+                RegisterSpec.make(destIndex, src.getType()),
+                src);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/InsnFormat.java b/dx/src/com/android/dx/dex/code/InsnFormat.java
new file mode 100644
index 0000000..ed4137b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/InsnFormat.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Base class for all instruction format handlers. Instruction format
+ * handlers know how to translate {@link DalvInsn} instances into
+ * streams of code words, as well as human-oriented listing strings
+ * representing such translations.
+ */
+public abstract class InsnFormat {
+    /**
+     * Returns the string form, suitable for inclusion in a listing
+     * dump, of the given instruction. The instruction must be of this
+     * instance's format for proper operation.
+     *
+     * @param insn non-null; the instruction
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return non-null; the string form
+     */
+    public final String listingString(DalvInsn insn, boolean noteIndices) {
+        String op = insn.getOpcode().getName();
+        String arg = insnArgString(insn);
+        String comment = insnCommentString(insn, noteIndices);
+        StringBuilder sb = new StringBuilder(100);
+
+        sb.append(op);
+
+        if (arg.length() != 0) {
+            sb.append(' ');
+            sb.append(arg);
+        }
+
+        if (comment.length() != 0) {
+            sb.append(" // ");
+            sb.append(comment);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the string form of the arguments to the given instruction.
+     * The instruction must be of this instance's format. If the instruction
+     * has no arguments, then the result should be <code>""</code>, not
+     * <code>null</code>.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn non-null; the instruction
+     * @return non-null; the string form
+     */
+    public abstract String insnArgString(DalvInsn insn);
+
+    /**
+     * Returns the associated comment for the given instruction, if any.
+     * The instruction must be of this instance's format. If the instruction
+     * has no comment, then the result should be <code>""</code>, not
+     * <code>null</code>.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn non-null; the instruction
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return non-null; the string form
+     */
+    public abstract String insnCommentString(DalvInsn insn,
+            boolean noteIndices);
+
+    /**
+     * Gets the code size of instructions that use this format. The
+     * size is a number of 16-bit code units, not bytes. This should
+     * throw an exception if this format is of variable size.
+     *
+     * @return &gt;= 0; the instruction length in 16-bit code units
+     */
+    public abstract int codeSize();
+
+    /**
+     * Returns whether or not the given instruction's arguments will
+     * fit in this instance's format. This includes such things as
+     * counting register arguments, checking register ranges, and
+     * making sure that additional arguments are of appropriate types
+     * and are in-range. If this format has a branch target but the
+     * instruction's branch offset is unknown, this method will simply
+     * not check the offset.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn non-null; the instruction to check
+     * @return <code>true</code> iff the instruction's arguments are
+     * appropriate for this instance, or <code>false</code> if not
+     */
+    public abstract boolean isCompatible(DalvInsn insn);
+
+    /**
+     * Returns whether or not the given instruction's branch offset will
+     * fit in this instance's format. This always returns <code>false</code>
+     * for formats that don't include a branch offset.
+     *
+     * <p>The default implementation of this method always returns
+     * <code>false</code>. Subclasses must override this method if they
+     * include branch offsets.</p>
+     *
+     * @param insn non-null; the instruction to check
+     * @return <code>true</code> iff the instruction's branch offset is
+     * appropriate for this instance, or <code>false</code> if not
+     */
+    public boolean branchFits(TargetInsn insn) {
+        return false;
+    }
+
+    /**
+     * Returns the next instruction format to try to match an instruction
+     * with, presuming that this instance isn't compatible, if any.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @return null-ok; the next format to try, or <code>null</code> if
+     * there are no suitable alternatives
+     */
+    public abstract InsnFormat nextUp();
+
+    /**
+     * Writes the code units for the given instruction to the given
+     * output destination. The instruction must be of this instance's format.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param out non-null; the output destination to write to
+     * @param insn non-null; the instruction to write
+     */
+    public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
+
+    /**
+     * Helper method to return a register list string.
+     *
+     * @param list non-null; the list of registers
+     * @return non-null; the string form
+     */
+    protected static String regListString(RegisterSpecList list) {
+        int sz = list.size();
+        StringBuffer sb = new StringBuffer(sz * 5 + 2);
+
+        sb.append('{');
+
+        for (int i = 0; i < sz; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(list.get(i).regString());
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a literal bits argument string.
+     *
+     * @param value the value
+     * @return non-null; the string form
+     */
+    protected static String literalBitsString(CstLiteralBits value) {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append('#');
+
+        if (value instanceof CstKnownNull) {
+            sb.append("null");
+        } else {
+            sb.append(value.typeName());
+            sb.append(' ');
+            sb.append(value.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a literal bits comment string.
+     *
+     * @param value the value
+     * @param width the width of the constant, in bits (used for displaying
+     * the uninterpreted bits; one of: <code>4 8 16 32 64</code>
+     * @return non-null; the comment
+     */
+    protected static String literalBitsComment(CstLiteralBits value,
+            int width) {
+        StringBuffer sb = new StringBuffer(20);
+
+        sb.append("#");
+
+        long bits;
+
+        if (value instanceof CstLiteral64) {
+            bits = ((CstLiteral64) value).getLongBits();
+        } else {
+            bits = value.getIntBits();
+        }
+
+        switch (width) {
+            case 4:  sb.append(Hex.uNibble((int) bits)); break;
+            case 8:  sb.append(Hex.u1((int) bits));      break;
+            case 16: sb.append(Hex.u2((int) bits));      break;
+            case 32: sb.append(Hex.u4((int) bits));      break;
+            case 64: sb.append(Hex.u8(bits));            break;
+            default: {
+                throw new RuntimeException("shouldn't happen");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a branch address string.
+     *
+     * @param insn non-null; the instruction in question
+     * @return non-null; the string form of the instruction's branch target
+     */
+    protected static String branchString(DalvInsn insn) {
+        TargetInsn ti = (TargetInsn) insn;
+        int address = ti.getTargetAddress();
+
+        return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
+    }
+
+    /**
+     * Helper method to return the comment for a branch.
+     *
+     * @param insn non-null; the instruction in question
+     * @return non-null; the comment
+     */
+    protected static String branchComment(DalvInsn insn) {
+        TargetInsn ti = (TargetInsn) insn;
+        int offset = ti.getTargetOffset();
+
+        return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
+    }
+
+    /**
+     * Helper method to return a constant string.
+     *
+     * @param insn non-null; a constant-bearing instruction
+     * @return non-null; the string form of the contained constant
+     */
+    protected static String cstString(DalvInsn insn) {
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return cst.toHuman();
+    }
+
+    /**
+     * Helper method to return an instruction comment for a constant.
+     *
+     * @param insn non-null; a constant-bearing instruction
+     * @return non-null; comment string representing the constant
+     */
+    protected static String cstComment(DalvInsn insn) {
+        CstInsn ci = (CstInsn) insn;
+
+        if (! ci.hasIndex()) {
+            return "";
+        }
+        
+        StringBuilder sb = new StringBuilder(20);
+        int index = ci.getIndex();
+
+        sb.append(ci.getConstant().typeName());
+        sb.append('@');
+
+        if (index < 65536) {
+            sb.append(Hex.u2(index));
+        } else {
+            sb.append(Hex.u4(index));
+        }
+        
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a nibble.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range -8..+7
+     */
+    protected static boolean signedFitsInNibble(int value) {
+        return (value >= -8) && (value <= 7);
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a nibble.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range 0..0xf
+     */
+    protected static boolean unsignedFitsInNibble(int value) {
+        return value == (value & 0xf);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a byte.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range -0x80..+0x7f
+     */
+    protected static boolean signedFitsInByte(int value) {
+        return (byte) value == value;
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a byte.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range 0..0xff
+     */
+    protected static boolean unsignedFitsInByte(int value) {
+        return value == (value & 0xff);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a short.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range -0x8000..+0x7fff
+     */
+    protected static boolean signedFitsInShort(int value) {
+        return (short) value == value;
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a short.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range 0..0xffff
+     */
+    protected static boolean unsignedFitsInShort(int value) {
+        return value == (value & 0xffff);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in three bytes.
+     *
+     * @param value the value in question
+     * @return <code>true</code> iff it's in the range -0x800000..+0x7fffff
+     */
+    protected static boolean signedFitsIn3Bytes(int value) {
+        return value == ((value << 8) >> 8);
+    }
+
+    /**
+     * Helper method to extract the callout-argument index from an
+     * appropriate instruction.
+     *
+     * @param insn non-null; the instruction
+     * @return &gt;= 0; the callout argument index
+     */
+    protected static int argIndex(DalvInsn insn) {
+        int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
+
+        if (arg < 0) {
+            throw new IllegalArgumentException("bogus insn");
+        }
+
+        return arg;
+    }
+
+    /**
+     * Helper method to combine an opcode and a second byte of data into
+     * the appropriate form for emitting into a code buffer.
+     *
+     * @param insn non-null; the instruction containing the opcode
+     * @param arg 0..255; arbitrary other byte value
+     * @return combined value
+     */
+    protected static short opcodeUnit(DalvInsn insn, int arg) {
+        if ((arg & 0xff) != arg) {
+            throw new IllegalArgumentException("arg out of range 0..255");
+        }
+
+        int opcode = insn.getOpcode().getOpcode();
+
+        if ((opcode & 0xff) != opcode) {
+            throw new IllegalArgumentException("opcode out of range 0..255");
+        }
+
+        return (short) (opcode | (arg << 8));
+    }
+
+    /**
+     * Helper method to combine two bytes into a code unit.
+     *
+     * @param low 0..255; low byte
+     * @param high 0..255; high byte
+     * @return combined value
+     */
+    protected static short codeUnit(int low, int high) {
+        if ((low & 0xff) != low) {
+            throw new IllegalArgumentException("low out of range 0..255");
+        }
+
+        if ((high & 0xff) != high) {
+            throw new IllegalArgumentException("high out of range 0..255");
+        }
+
+        return (short) (low | (high << 8));
+    }
+
+    /**
+     * Helper method to combine four nibbles into a code unit.
+     *
+     * @param n0 0..15; low nibble
+     * @param n1 0..15; medium-low nibble
+     * @param n2 0..15; medium-high nibble
+     * @param n3 0..15; high nibble
+     * @return combined value
+     */
+    protected static short codeUnit(int n0, int n1, int n2, int n3) {
+        if ((n0 & 0xf) != n0) {
+            throw new IllegalArgumentException("n0 out of range 0..15");
+        }
+
+        if ((n1 & 0xf) != n1) {
+            throw new IllegalArgumentException("n1 out of range 0..15");
+        }
+
+        if ((n2 & 0xf) != n2) {
+            throw new IllegalArgumentException("n2 out of range 0..15");
+        }
+
+        if ((n3 & 0xf) != n3) {
+            throw new IllegalArgumentException("n3 out of range 0..15");
+        }
+
+        return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
+    }
+
+    /**
+     * Helper method to combine two nibbles into a byte.
+     *
+     * @param low 0..15; low nibble
+     * @param high 0..15; high nibble
+     * @return 0..255; combined value
+     */
+    protected static int makeByte(int low, int high) {
+        if ((low & 0xf) != low) {
+            throw new IllegalArgumentException("low out of range 0..15");
+        }
+
+        if ((high & 0xf) != high) {
+            throw new IllegalArgumentException("high out of range 0..15");
+        }
+
+        return low | (high << 4);
+    }
+
+    /**
+     * Writes one code unit to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0) {
+        out.writeShort(c0);
+    }
+
+    /**
+     * Writes two code units to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+    }
+
+    /**
+     * Writes three code units to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+    }
+
+    /**
+     * Writes four code units to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+    }
+
+    /**
+     * Writes five code units to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     * @param c4 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3, short c4) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+        out.writeShort(c4);
+    }
+
+    /**
+     * Writes six code units to the given output destination.
+     * 
+     * @param out non-null; where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     * @param c4 code unit to write
+     * @param c5 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3, short c4, short c5) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+        out.writeShort(c4);
+        out.writeShort(c5);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalIntroduction.java b/dx/src/com/android/dx/dex/code/LocalIntroduction.java
new file mode 100644
index 0000000..c085a5e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalIntroduction.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to introduce a new local variable. That
+ * is, an instance of this class in an instruction stream indicates that
+ * starting with the subsequent instruction, the indicated variable
+ * is set.
+ */
+public final class LocalIntroduction extends ZeroSizeInsn {
+    /**
+     * non-null; register spec representing the local variable introduced
+     * by this instance 
+     */
+    private final RegisterSpec local;
+
+    /**
+     * Returns the local variable listing string for a single register spec.
+     * 
+     * @param spec non-null; the spec to convert
+     * @return non-null; the string form
+     */
+    public static String localString(RegisterSpec spec) {
+        return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " +
+            spec.getTypeBearer().toHuman();
+    }
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param local non-null; register spec representing the local
+     * variable introduced by this instance
+     */
+    public LocalIntroduction(SourcePosition position, RegisterSpec local) {
+        super(position);
+
+        if (local == null) {
+            throw new NullPointerException("local == null");
+        }
+
+        this.local = local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return new LocalIntroduction(getPosition(), local.withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new LocalIntroduction(getPosition(), local);
+    }
+
+    /**
+     * Gets the register spec representing the local variable introduced
+     * by this instance.
+     * 
+     * @return non-null; the register spec
+     */
+    public RegisterSpec getLocal() {
+        return local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return local.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        return "local-intro " + localString(local);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java
new file mode 100644
index 0000000..16c0192
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalList.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+import java.util.ArrayList;
+
+/**
+ * List of local variables. Each local variable entry indicates a
+ * range of code which it is valid for, a register number, a name,
+ * and a type.
+ */
+public final class LocalList extends FixedSizeList {
+    /** non-null; empty instance */
+    public static final LocalList EMPTY = new LocalList(0);
+
+    /**
+     * Constructs an instance for the given method, based on the given
+     * block order and intermediate local information.
+     * 
+     * @param insns non-null; instructions to convert
+     * @return non-null; the constructed list 
+     */
+    public static LocalList make(DalvInsnList insns) {
+        ArrayList<Entry> result = new ArrayList<Entry>(100);
+        int codeSize = insns.codeSize();
+        int sz = insns.size();
+        RegisterSpecSet state = null;
+        int stateMax = 0;
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = insns.get(i);
+
+            if (insn instanceof LocalSnapshot) {
+                RegisterSpecSet newState = ((LocalSnapshot) insn).getLocals();
+                boolean first = (state == null);
+
+                if (first) {
+                    stateMax = newState.getMaxSize();
+                }
+
+                for (int j = 0; j < stateMax; j++) {
+                    RegisterSpec oldSpec = first ? null : state.get(j);
+                    RegisterSpec newSpec = newState.get(j);
+                    boolean oldEnds = false;
+                    boolean newStarts = false;
+
+                    if (oldSpec == null) {
+                        if (newSpec != null) {
+                            /*
+                             * This is a newly-introduced local, not
+                             * replacing an existing local.
+                             */
+                            newStarts = true;
+                        }
+                    } else if (newSpec == null) {
+                        /*
+                         * This is a local going out of scope, with no
+                         * replacement.
+                         */
+                        oldEnds = true;
+                    } else if (!oldSpec.equals(newSpec)) {
+                        /*
+                         * This is a local going out of scope, immediately
+                         * replaced by a different local.
+                         */
+                        oldEnds = true;
+                        newStarts = true;
+                    }
+
+                    if (oldEnds) {
+                        endScope(result, oldSpec, insn.getAddress());
+                    }
+
+                    if (newStarts) {
+                        startScope(result, newSpec, insn.getAddress(),
+                                   codeSize);
+                    }
+                }
+
+                state = newState;
+            } else if (insn instanceof LocalIntroduction) {
+                RegisterSpec newSpec = ((LocalIntroduction) insn).getLocal();
+                RegisterSpec oldSpec = state.get(newSpec);
+
+                boolean oldEnds = false;
+                boolean newStarts = false;
+
+                if (oldSpec == null) {
+                    /*
+                     * This is a newly-introduced local, not replacing an
+                     * existing local.
+                     */
+                    newStarts = true;
+                } else if (!oldSpec.equals(newSpec)) {
+                    /*
+                     * This is a local going out of scope, immediately
+                     * replaced by a different local.
+                     */
+                    oldEnds = true;
+                    newStarts = true;
+                }
+
+                if (newStarts) {
+                    int address = insn.getAddress();
+
+                    if (oldEnds) {
+                        endScope(result, oldSpec, address);
+                    }
+
+                    startScope(result, newSpec, address, codeSize);
+
+                    if (state.isImmutable()) {
+                        state = state.mutableCopy();
+                    }
+
+                    state.put(newSpec);
+                }
+            }
+        }
+
+        int resultSz = result.size();
+
+        if (resultSz == 0) {
+            return EMPTY;
+        }
+
+        LocalList resultList = new LocalList(resultSz);
+
+        for (int i = 0; i < resultSz; i++) {
+            resultList.set(i, result.get(i));
+        }
+
+        resultList.setImmutable();
+        return resultList;
+    }
+
+    /**
+     * Helper for {@link #make}, to indicate that the given variable has
+     * been introduced.
+     * 
+     * @param result non-null; result in-progress
+     * @param spec non-null; register spec for the variable in question
+     * @param startAddress &gt;= 0; address at which the scope starts
+     * (inclusive)
+     * @param endAddress &gt; startAddress; initial scope end address
+     * (exclusive)
+     */
+    private static void startScope(ArrayList<Entry> result, RegisterSpec spec,
+                                   int startAddress, int endAddress) {
+        result.add(new Entry(startAddress, endAddress, spec));
+    }
+
+    /**
+     * Helper for {@link #make}, to indicate that the given variable's
+     * scope has closed.
+     * 
+     * @param result non-null; result in-progress
+     * @param spec non-null; register spec for the variable in question
+     * @param endAddress &gt;= 0; address at which the scope ends (exclusive)
+     */
+    private static void endScope(ArrayList<Entry> result, RegisterSpec spec,
+                                 int endAddress) {
+        int sz = result.size();
+
+        for (int i = sz - 1; i >= 0; i--) {
+            Entry e = result.get(i);
+            if (e.matches(spec)) {
+                if (e.getStart() == endAddress) {
+                    /*
+                     * It turns out that the indicated entry doesn't actually
+                     * cover any code.
+                     */
+                    result.remove(i);
+                } else {
+                    result.set(i, e.withEnd(endAddress));
+                }
+                return;
+            }
+        }
+
+        throw new RuntimeException("unmatched variable: " + spec);
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size &gt;= 0; the size of the list
+     */
+    public LocalList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param start &gt;= 0; start address 
+     * @param end &gt; start; end address (exclusive)
+     * @param spec non-null; register spec representing the variable
+     */
+    public void set(int n, int start, int end, RegisterSpec spec) {
+        set0(n, new Entry(start, end, spec));
+    }
+
+    /**
+     * Sets the entry at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param entry non-null; the entry to set at <code>n</code>
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /**
+     * Entry in a local list.
+     */
+    public static class Entry {
+        /** &gt;= 0; start address */
+        private final int start;
+
+        /** &gt; start; end address (exclusive) */
+        private final int end;
+
+        /** non-null; register spec representing the variable */
+        private final RegisterSpec spec;
+
+        /** non-null; variable type */
+        private final CstType type;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param start &gt;= 0; start address 
+         * @param end &gt; start; end address (exclusive)
+         * @param spec non-null; register spec representing the variable
+         */
+        public Entry(int start, int end, RegisterSpec spec) {
+            if (start < 0) {
+                throw new IllegalArgumentException("start < 0");
+            }
+
+            if (end <= start) {
+                throw new IllegalArgumentException("end <= start");
+            }
+
+            try {
+                if (spec.getLocalItem() == null) {
+                    throw new NullPointerException(
+                            "spec.getLocalItem() == null");
+                }
+            } catch (NullPointerException ex) {
+                // Elucidate the exception.
+                throw new NullPointerException("spec == null");
+            }
+
+            this.start = start;
+            this.end = end;
+            this.spec = spec;
+
+            if (spec.getType() == Type.KNOWN_NULL) {
+                /*
+                 * KNOWN_NULL's descriptor is '<null>', which we do
+                 * not want to emit. Everything else is as expected.
+                 */
+                this.type = CstType.OBJECT;
+            } else {
+                this.type = CstType.intern(spec.getType());
+            }
+        }
+
+        /**
+         * Gets the start address.
+         * 
+         * @return &gt;= 0; the start address
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Gets the end address (exclusive).
+         * 
+         * @return &gt; start; the end address (exclusive)
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the variable name.
+         * 
+         * @return null-ok; the variable name
+         */
+        public CstUtf8 getName() {
+            return spec.getLocalItem().getName();
+        }
+
+        /**
+         * Gets the variable signature.
+         *
+         * @return null-ok; the variable signature
+         */
+        public CstUtf8 getSignature() {
+            return spec.getLocalItem().getSignature();
+        }
+
+        /**
+         * Gets the variable's type.
+         * 
+         * @return non-null; the type
+         */
+        public CstType getType() {
+            return type;
+        }
+
+        /**
+         * Gets the number of the register holding the variable.
+         * 
+         * @return &gt;= 0; the number fo the register holding the variable
+         */
+        public int getRegister() {
+            return spec.getReg();
+        }
+
+        /**
+         * Gets the RegisterSpec of the register holding the variable.
+         *
+         * @return non-null; RegisterSpec of the holding register.
+         */
+        public RegisterSpec getRegisterSpec() {
+            return spec;
+        }
+
+        /**
+         * Returns whether or not this instance matches the given spec.
+         * 
+         * @param spec non-null; the spec in question
+         * @return <code>true</code> iff this instance matches
+         * <code>spec</code>
+         */
+        public boolean matches(RegisterSpec spec) {
+            return spec.equals(this.spec);
+        }
+
+        /**
+         * Returns whether or not this instance matches the spec in
+         * the given instance.
+         *
+         * @param other non-null; another entry
+         * @return <code>true</code> iff this instance's spec matches
+         * <code>other</code>
+         */
+        public boolean matches(Entry other) {
+            return other.spec.equals(this.spec);
+        }
+
+        /**
+         * Returns an instance just like this one, except with the end
+         * address altered to be the one given.
+         * 
+         * @param newEnd &gt; getStart(); the end address of the new instance
+         * @return non-null; an appropriately-constructed instance
+         */
+        public Entry withEnd(int newEnd) {
+            return new Entry(start, newEnd, spec);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalSnapshot.java b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
new file mode 100644
index 0000000..616104a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to hold a snapshot of the
+ * state of local variable name mappings that exists immediately after
+ * the instance in an instruction array.
+ */
+public final class LocalSnapshot extends ZeroSizeInsn {
+    /** non-null; local state associated with this instance */
+    private final RegisterSpecSet locals;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param locals non-null; associated local variable state
+     */
+    public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) {
+        super(position);
+
+        if (locals == null) {
+            throw new NullPointerException("locals == null");
+        }
+
+        this.locals = locals;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return new LocalSnapshot(getPosition(), locals.withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new LocalSnapshot(getPosition(), locals);
+    }
+
+    /**
+     * Gets the local state associated with this instance.
+     * 
+     * @return non-null; the state
+     */
+    public RegisterSpecSet getLocals() {
+        return locals;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return locals.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int sz = locals.size();
+        int max = locals.getMaxSize();
+        StringBuffer sb = new StringBuffer(100 + sz * 40);
+
+        sb.append("local-snapshot");
+
+        for (int i = 0; i < max; i++) {
+            RegisterSpec spec = locals.get(i);
+            if (spec != null) {
+                sb.append("\n  ");
+                sb.append(LocalIntroduction.localString(spec));
+            }
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OddSpacer.java b/dx/src/com/android/dx/dex/code/OddSpacer.java
new file mode 100644
index 0000000..f99df36
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OddSpacer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction which either turns into a <code>nop</code> or
+ * nothingness, in order to make the subsequent instruction have an
+ * even address. This is used to align (subsequent) instructions that
+ * require it.
+ */
+public final class OddSpacer extends VariableSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     */
+    public OddSpacer(SourcePosition position) {
+        super(position, RegisterSpecList.EMPTY);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return (getAddress() & 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        if (codeSize() != 0) {
+            out.writeShort(InsnFormat.codeUnit(DalvOps.NOP, 0));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new OddSpacer(getPosition());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        if (codeSize() == 0) {
+            return null;
+        }
+
+        return "nop // spacer";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputCollector.java b/dx/src/com/android/dx/dex/code/OutputCollector.java
new file mode 100644
index 0000000..98d8a9c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputCollector.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import java.util.ArrayList;
+
+/**
+ * Destination for {@link DalvInsn} instances being output. This class
+ * receives and collects instructions in two pieces &mdash; a primary
+ * list and a suffix (generally consisting of adjunct data referred to
+ * by the primary list, such as switch case tables) &mdash; which it
+ * merges and emits back out in the form of a {@link DalvInsnList}
+ * instance.
+ */
+public final class OutputCollector {
+    /**
+     * non-null; the associated finisher (which holds the instruction
+     * list in-progress)
+     */
+    private final OutputFinisher finisher;
+
+    /**
+     * null-ok; suffix for the output, or <code>null</code> if the suffix
+     * has been appended to the main output (by {@link #appendSuffixToOutput})
+     */
+    private ArrayList<DalvInsn> suffix;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param initialCapacity &gt;= 0; initial capacity of the output list
+     * @param suffixInitialCapacity &gt;= 0; initial capacity of the output
+     * suffix
+     * @param regCount &gt;= 0; register count for the method
+     */
+    public OutputCollector(int initialCapacity, int suffixInitialCapacity,
+            int regCount) {
+        this.finisher = new OutputFinisher(initialCapacity, regCount);
+        this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity);
+    }
+
+    /**
+     * Adds an instruction to the output.
+     * 
+     * @param insn non-null; the instruction to add 
+     */
+    public void add(DalvInsn insn) {
+        finisher.add(insn);
+    }
+
+    /**
+     * Reverses a branch which is buried a given number of instructions
+     * backward in the output. It is illegal to call this unless the
+     * indicated instruction really is a reversible branch.
+     * 
+     * @param which how many instructions back to find the branch;
+     * <code>0</code> is the most recently added instruction,
+     * <code>1</code> is the instruction before that, etc.
+     * @param newTarget non-null; the new target for the reversed branch
+     */
+    public void reverseBranch(int which, CodeAddress newTarget) {
+        finisher.reverseBranch(which, newTarget);
+    }
+
+    /**
+     * Adds an instruction to the output suffix.
+     * 
+     * @param insn non-null; the instruction to add 
+     */
+    public void addSuffix(DalvInsn insn) {
+        suffix.add(insn);
+    }
+
+    /**
+     * Gets the results of all the calls on this instance, in the form of
+     * an {@link OutputFinisher}.
+     *
+     * @return non-null; the output finisher
+     * @throws UnsupportedOperationException if this method has
+     * already been called
+     */
+    public OutputFinisher getFinisher() {
+        if (suffix == null) {
+            throw new UnsupportedOperationException("already processed");
+        }
+        
+        appendSuffixToOutput();
+        return finisher;
+    }
+
+    /**
+     * Helper for {@link #getFinisher}, which appends the suffix to
+     * the primary output.
+     */
+    private void appendSuffixToOutput() {
+        int size = suffix.size();
+
+        for (int i = 0; i < size; i++) {
+            finisher.add(suffix.get(i));
+        }
+
+        suffix = null;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputFinisher.java b/dx/src/com/android/dx/dex/code/OutputFinisher.java
new file mode 100644
index 0000000..f90e970
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Processor for instruction lists, which takes a "first cut" of
+ * instruction selection as a basis and produces a "final cut" in the
+ * form of a {@link DalvInsnList} instance.
+ */
+public final class OutputFinisher {
+    /**
+     * &gt;= 0; register count for the method, not including any extra
+     * "reserved" registers needed to translate "difficult" instructions
+     */
+    private final int unreservedRegCount;
+
+    /** non-null; the list of instructions, per se */
+    private ArrayList<DalvInsn> insns;
+
+    /** whether any instruction has position info */
+    private boolean hasAnyPositionInfo;
+
+    /** whether any instruction has local variable info */
+    private boolean hasAnyLocalInfo;
+
+    /**
+     * &gt;= 0; the count of reserved registers (low-numbered
+     * registers used when expanding instructions that can't be
+     * represented simply); becomes valid after a call to {@link
+     * #massageInstructions}
+     */
+    private int reservedCount;
+
+    /**
+     * Constructs an instance. It initially contains no instructions.
+     * 
+     * @param regCount &gt;= 0; register count for the method
+     * @param initialCapacity &gt;= 0; initial capacity of the instructions
+     * list
+     */
+    public OutputFinisher(int initialCapacity, int regCount) {
+        this.unreservedRegCount = regCount;
+        this.insns = new ArrayList<DalvInsn>(initialCapacity);
+        this.reservedCount = -1;
+        this.hasAnyPositionInfo = false;
+        this.hasAnyLocalInfo = false;
+    }
+
+    /**
+     * Returns whether any of the instructions added to this instance
+     * come with position info.
+     * 
+     * @return whether any of the instructions added to this instance
+     * come with position info
+     */
+    public boolean hasAnyPositionInfo() {
+        return hasAnyPositionInfo;
+    }
+    
+    /**
+     * Returns whether this instance has any local variable information.
+     * 
+     * @return whether this instance has any local variable information
+     */
+    public boolean hasAnyLocalInfo() {
+        return hasAnyLocalInfo;
+    }
+
+    /**
+     * Helper for {@link #add} which scrutinizes a single
+     * instruction for local variable information.
+     * 
+     * @param insn non-null; instruction to scrutinize
+     * @return <code>true</code> iff the instruction refers to any
+     * named locals
+     */
+    private static boolean hasLocalInfo(DalvInsn insn) {
+        if (insn instanceof LocalSnapshot) {
+            RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+            int size = specs.size();
+            for (int i = 0; i < size; i++) {
+                if (hasLocalInfo(specs.get(i))) {
+                    return true;
+                }
+            }
+        } else if (insn instanceof LocalIntroduction) {
+            RegisterSpec spec = ((LocalIntroduction) insn).getLocal();
+            if (hasLocalInfo(spec)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
+     * register spec.
+     * 
+     * @param spec non-null; spec to scrutinize
+     * @return <code>true</code> iff the spec refers to any
+     * named locals
+     */
+    private static boolean hasLocalInfo(RegisterSpec spec) {
+        return (spec != null)
+            && (spec.getLocalItem().getName() != null);
+    }
+
+    /**
+     * Returns the set of all constants referred to by instructions added
+     * to this instance.
+     * 
+     * @return non-null; the set of constants
+     */
+    public HashSet<Constant> getAllConstants() {
+        HashSet<Constant> result = new HashSet<Constant>(20);
+
+        for (DalvInsn insn : insns) {
+            addConstants(result, insn);
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #getAllConstants} which adds all the info for
+     * a single instruction.
+     * 
+     * @param result non-null; result set to add to
+     * @param insn non-null; instruction to scrutinize
+     */
+    private static void addConstants(HashSet<Constant> result,
+            DalvInsn insn) {
+        if (insn instanceof CstInsn) {
+            Constant cst = ((CstInsn) insn).getConstant();
+            result.add(cst);
+        } else if (insn instanceof LocalSnapshot) {
+            RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+            int size = specs.size();
+            for (int i = 0; i < size; i++) {
+                addConstants(result, specs.get(i));
+            }
+        } else if (insn instanceof LocalIntroduction) {
+            RegisterSpec spec = ((LocalIntroduction) insn).getLocal();
+            addConstants(result, spec);
+        }
+    }
+
+    /**
+     * Helper for {@link #getAllConstants} which adds all the info for
+     * a single <code>RegisterSpec</code>.
+     *
+     * @param result non-null; result set to add to
+     * @param spec null-ok; register spec to add
+     */
+    private static void addConstants(HashSet<Constant> result,
+            RegisterSpec spec) {
+        if (spec == null) {
+            return;
+        }
+        
+        LocalItem local = spec.getLocalItem();
+        CstUtf8 name = local.getName();
+        CstUtf8 signature = local.getSignature();
+        Type type = spec.getType();
+
+        if (type != Type.KNOWN_NULL) {
+            result.add(CstType.intern(type));
+        }
+
+        if (name != null) {
+            result.add(name);
+        }
+
+        if (signature != null) {
+            result.add(signature);
+        }
+    }
+
+    /**
+     * Adds an instruction to the output.
+     * 
+     * @param insn non-null; the instruction to add 
+     */
+    public void add(DalvInsn insn) {
+        insns.add(insn);
+        updateInfo(insn);
+    }
+
+    /**
+     * Inserts an instruction in the output at the given offset.
+     * 
+     * @param at &gt;= 0; what index to insert at
+     * @param insn non-null; the instruction to insert
+     */
+    public void insert(int at, DalvInsn insn) {
+        insns.add(at, insn);
+        updateInfo(insn);
+    }
+
+    /**
+     * Helper for {@link #add} and {@link #insert},
+     * which updates the position and local info flags.
+     * 
+     * @param insn non-null; an instruction that was just introduced
+     */
+    private void updateInfo(DalvInsn insn) {
+        if (! hasAnyPositionInfo) {
+            SourcePosition pos = insn.getPosition();
+            if (pos.getLine() >= 0) {
+                hasAnyPositionInfo = true;
+            }
+        }
+
+        if (! hasAnyLocalInfo) {
+            if (hasLocalInfo(insn)) {
+                hasAnyLocalInfo = true;
+            }
+        }
+    }
+
+    /**
+     * Reverses a branch which is buried a given number of instructions
+     * backward in the output. It is illegal to call this unless the
+     * indicated instruction really is a reversible branch.
+     * 
+     * @param which how many instructions back to find the branch;
+     * <code>0</code> is the most recently added instruction,
+     * <code>1</code> is the instruction before that, etc.
+     * @param newTarget non-null; the new target for the reversed branch
+     */
+    public void reverseBranch(int which, CodeAddress newTarget) {
+        int size = insns.size();
+        int index = size - which - 1;
+        TargetInsn targetInsn;
+
+        try {
+            targetInsn = (TargetInsn) insns.get(index);
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("too few instructions");
+        } catch (ClassCastException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("non-reversible instruction");
+        }
+
+        /*
+         * No need to call this.set(), since the format and other info
+         * are the same.
+         */
+        insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
+    }
+
+    /**
+     * Assigns indices in all instructions that need them, using the
+     * given callback to perform lookups. This should be called before
+     * calling {@link #finishProcessingAndGetList}.
+     * 
+     * @param callback non-null; callback object
+     */
+    public void assignIndices(DalvCode.AssignIndicesCallback callback) {
+        for (DalvInsn insn : insns) {
+            if (insn instanceof CstInsn) {
+                assignIndices((CstInsn) insn, callback);
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #assignIndices} which does assignment for one
+     * instruction.
+     * 
+     * @param insn non-null; the instruction
+     * @param callback non-null; the callback
+     */
+    private static void assignIndices(CstInsn insn,
+            DalvCode.AssignIndicesCallback callback) {
+        Constant cst = insn.getConstant();
+        int index = callback.getIndex(cst);
+
+        if (index >= 0) {
+            insn.setIndex(index);
+        }
+
+        if (cst instanceof CstMemberRef) {
+            CstMemberRef member = (CstMemberRef) cst;
+            CstType definer = member.getDefiningClass();
+            index = callback.getIndex(definer);
+            if (index >= 0) {
+                insn.setClassIndex(index);
+            }
+        }
+    }
+
+    /**
+     * Does final processing on this instance and gets the output as
+     * a {@link DalvInsnList}. Final processing consists of:
+     * 
+     * <ul>
+     *   <li>optionally renumbering registers (to make room as needed for
+     *   expanded instructions)</li>
+     *   <li>picking a final opcode for each instruction</li>
+     *   <li>rewriting instructions, because of register number,
+     *   constant pool index, or branch target size issues</li>
+     *   <li>assigning final addresses</li>
+     * </ul>
+     * 
+     * <p><b>Note:</b> This method may only be called once per instance
+     * of this class.</p>
+     *
+     * @return non-null; the output list
+     * @throws UnsupportedOperationException if this method has
+     * already been called
+     */
+    public DalvInsnList finishProcessingAndGetList() {
+        if (reservedCount >= 0) {
+            throw new UnsupportedOperationException("already processed");
+        }
+
+        InsnFormat[] formats = makeFormatsArray();
+        reserveRegisters(formats);
+        massageInstructions(formats);
+        assignAddressesAndFixBranches();
+
+        return DalvInsnList.makeImmutable(insns,
+                reservedCount + unreservedRegCount);
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which extracts
+     * the format out of each instruction into a separate array, to be
+     * further manipulated as things progress.
+     * 
+     * @return non-null; the array of formats
+     */
+    private InsnFormat[] makeFormatsArray() {
+        int size = insns.size();
+        InsnFormat[] result = new InsnFormat[size];
+
+        for (int i = 0; i < size; i++) {
+            result[i] = insns.get(i).getOpcode().getFormat();
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which figures
+     * out how many reserved registers are required and then reserving
+     * them. It also updates the given <code>formats</code> array so
+     * as to avoid extra work when constructing the massaged
+     * instruction list.
+     * 
+     * @param formats non-null; array of per-instruction format selections
+     */
+    private void reserveRegisters(InsnFormat[] formats) {
+        int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
+        
+        /*
+         * Call calculateReservedCount() and then perform register
+         * reservation, repeatedly until no new reservations happen.
+         */
+        for (;;) {
+            int newReservedCount = calculateReservedCount(formats);
+            if (oldReservedCount >= newReservedCount) {
+                break;
+            }
+
+            int reservedDifference = newReservedCount - oldReservedCount;
+            int size = insns.size();
+
+            for (int i = 0; i < size; i++) {
+                /*
+                 * CodeAddress instance identity is used to link
+                 * TargetInsns to their targets, so it is
+                 * inappropriate to make replacements, and they don't
+                 * have registers in any case. Hence, the instanceof
+                 * test below.
+                 */
+                DalvInsn insn = insns.get(i);
+                if (!(insn instanceof CodeAddress)) {
+                    /*
+                     * No need to call this.set() since the format and
+                     * other info are the same.
+                     */ 
+                    insns.set(i, insn.withRegisterOffset(reservedDifference));
+                }
+            }
+
+            oldReservedCount = newReservedCount;
+        }
+
+        reservedCount = oldReservedCount;
+    }
+
+    /**
+     * Helper for {@link #reserveRegisters}, which does one
+     * pass over the instructions, calculating the number of
+     * registers that need to be reserved. It also updates the
+     * <code>formats</code> list to help avoid extra work in future
+     * register reservation passes.
+     * 
+     * @param formats non-null; array of per-instruction format selections
+     * @return &gt;= 0; the count of reserved registers
+     */
+    private int calculateReservedCount(InsnFormat[] formats) {
+        int size = insns.size();
+
+        /*
+         * Potential new value of reservedCount, which gets updated in the
+         * following loop. It starts out with the existing reservedCount
+         * and gets increased if it turns out that additional registers
+         * need to be reserved.
+         */
+        int newReservedCount = reservedCount;
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            InsnFormat originalFormat = formats[i];
+            InsnFormat newFormat = findFormatForInsn(insn, originalFormat);
+
+            if (originalFormat == newFormat) {
+                continue;
+            }
+            
+            if (newFormat == null) {
+                /*
+                 * The instruction will need to be expanded, so reserve
+                 * registers for it.
+                 */
+                int reserve = insn.getMinimumRegisterRequirement();
+                if (reserve > newReservedCount) {
+                    newReservedCount = reserve;
+                }
+            }
+
+            formats[i] = newFormat;
+        }
+
+        return newReservedCount;
+    }
+
+    /**
+     * Attempts to fit the given instruction into a format, returning
+     * either a format that the instruction fits into or <code>null</code>
+     * to indicate that the instruction will need to be expanded. This
+     * fitting process starts with the given format as a first "best
+     * guess" and then pessimizes from there if necessary.
+     *
+     * @param insn non-null; the instruction in question
+     * @param format null-ok; the current guess as to the best instruction
+     * format to use; <code>null</code> means that no simple format fits
+     * @return null-ok; a possibly-different format, which is either a
+     * good fit or <code>null</code> to indicate that no simple format
+     * fits
+     */
+    private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) {
+        if (format == null) {
+            // The instruction is already known not to fit any simple format.
+            return format;
+        }
+
+        if (format.isCompatible(insn)) {
+            // The instruction already fits in the current best-known format.
+            return format;
+        }
+
+        Dop dop = insn.getOpcode();
+        int family = dop.getFamily();
+
+        for (;;) {
+            format = format.nextUp();
+            if ((format == null) ||
+                    (format.isCompatible(insn) && 
+                     (Dops.getOrNull(family, format) != null))) {
+                break;
+            }
+        }
+
+        return format;
+    }
+    
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which goes
+     * through each instruction in the output, making sure its opcode
+     * can accomodate its arguments. In cases where the opcode is
+     * unable to do so, this replaces the instruction with a larger
+     * instruction with identical semantics that <i>will</i> work.
+     * 
+     * <p>This method may also reserve a number of low-numbered
+     * registers, renumbering the instructions' original registers, in
+     * order to have register space available in which to move
+     * very-high registers when expanding instructions into
+     * multi-instruction sequences. This expansion is done when no
+     * simple instruction format can be found for a given instruction that
+     * is able to accomodate that instruction's registers.</p>
+     * 
+     * <p>This method ignores issues of branch target size, since
+     * final addresses aren't known at the point that this method is
+     * called.</p>
+     * 
+     * @param formats non-null; array of per-instruction format selections
+     */
+    private void massageInstructions(InsnFormat[] formats) {
+        if (reservedCount == 0) {
+            /*
+             * The easy common case: No registers were reserved, so we
+             * merely need to replace any instructions whose format changed
+             * during the reservation pass, but all instructions will stay
+             * at their original indices, and the instruction list doesn't
+             * grow.
+             */
+            int size = insns.size();
+
+            for (int i = 0; i < size; i++) {
+                DalvInsn insn = insns.get(i);
+                Dop dop = insn.getOpcode();
+                InsnFormat format = formats[i];
+
+                if (format != dop.getFormat()) {
+                    dop = Dops.getOrNull(dop.getFamily(), format);
+                    insns.set(i, insn.withOpcode(dop));
+                }
+            }
+        } else {
+            /*
+             * The difficult uncommon case: Some instructions have to be
+             * expanded to deal with high registers.
+             */
+            insns = performExpansion(formats);
+        }
+    }
+
+    /**
+     * Helper for {@link #massageInstructions}, which constructs a
+     * replacement list, where each {link DalvInsn} instance that
+     * couldn't be represented simply (due to register representation
+     * problems) is expanded into a series of instances that together
+     * perform the proper function.
+     * 
+     * @param formats non-null; array of per-instruction format selections
+     * @return non-null; the replacement list
+     */
+    private ArrayList<DalvInsn> performExpansion(InsnFormat[] formats) {
+        int size = insns.size();
+        ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            Dop dop = insn.getOpcode();
+            InsnFormat originalFormat = dop.getFormat();
+            InsnFormat currentFormat = formats[i];
+            DalvInsn prefix;
+            DalvInsn suffix;
+
+            if (currentFormat != null) {
+                // No expansion is necessary.
+                prefix = null;
+                suffix = null;
+            } else {
+                // Expansion is required.
+                prefix = insn.hrPrefix();
+                suffix = insn.hrSuffix();
+
+                /*
+                 * Get the initial guess as to the hr version, but then
+                 * let findFormatForInsn() pick a better format, if any.
+                 */
+                insn = insn.hrVersion();
+                originalFormat = insn.getOpcode().getFormat();
+                currentFormat = findFormatForInsn(insn, originalFormat);
+            }
+
+            if (prefix != null) {
+                result.add(prefix);
+            }
+            
+            if (currentFormat != originalFormat) {
+                dop = Dops.getOrNull(dop.getFamily(), currentFormat);
+                insn = insn.withOpcode(dop);
+            }
+            result.add(insn);
+
+            if (suffix != null) {
+                result.add(suffix);
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which assigns
+     * addresses to each instruction, possibly rewriting branches to
+     * fix ones that wouldn't otherwise be able to reach their
+     * targets.
+     */
+    private void assignAddressesAndFixBranches() {
+        for (;;) {
+            assignAddresses();
+            if (!fixBranches()) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #assignAddressesAndFixBranches}, which
+     * assigns an address to each instruction, in order.
+     */
+    private void assignAddresses() {
+        int address = 0;
+        int size = insns.size();
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            insn.setAddress(address);
+            address += insn.codeSize();
+        }
+    }
+
+    /**
+     * Helper for {@link #assignAddressesAndFixBranches}, which checks
+     * the branch target size requirement of each branch instruction
+     * to make sure it fits. For instructions that don't fit, this
+     * rewrites them to use a <code>goto</code> of some sort. In the
+     * case of a conditional branch that doesn't fit, the sense of the
+     * test is reversed in order to branch around a <code>goto</code>
+     * to the original target.
+     * 
+     * @return whether any branches had to be fixed
+     */
+    private boolean fixBranches() {
+        int size = insns.size();
+        boolean anyFixed = false;
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            if (!(insn instanceof TargetInsn)) {
+                // This loop only needs to inspect TargetInsns.
+                continue;
+            }
+
+            Dop dop = insn.getOpcode();
+            InsnFormat format = dop.getFormat();
+            TargetInsn target = (TargetInsn) insn;
+
+            if (format.branchFits(target)) {
+                continue;
+            }
+
+            if (dop.getFamily() == DalvOps.GOTO) {
+                // It is a goto; widen it if possible.
+                InsnFormat newFormat = findFormatForInsn(insn, format);
+                if (newFormat == null) {
+                    /*
+                     * The branch is already maximally large. This should
+                     * only be possible if a method somehow manages to have
+                     * more than 2^31 code units.
+                     */
+                    throw new UnsupportedOperationException("method too long");
+                }
+                dop = Dops.getOrNull(dop.getFamily(), newFormat);
+                insn = insn.withOpcode(dop);
+                insns.set(i, insn);
+            } else {
+                /*
+                 * It is a conditional: Reverse its sense, and arrange for
+                 * it to branch around an absolute goto to the original
+                 * branch target.
+                 * 
+                 * Note: An invariant of the list being processed is
+                 * that every TargetInsn is followed by a CodeAddress.
+                 * Hence, it is always safe to get the next element
+                 * after a TargetInsn and cast it to CodeAddress, as
+                 * is happening a few lines down.
+                 * 
+                 * Also note: Size gets incremented by one here, as we
+                 * have -- in the net -- added one additional element
+                 * to the list, so we increment i to match. The added
+                 * and changed elements will be inspected by a repeat
+                 * call to this method after this invocation returns.
+                 */
+                CodeAddress newTarget;
+                try {
+                    newTarget = (CodeAddress) insns.get(i + 1);
+                } catch (IndexOutOfBoundsException ex) {
+                    // The TargetInsn / CodeAddress invariant was violated.
+                    throw new IllegalStateException(
+                            "unpaired TargetInsn (dangling)");
+                } catch (ClassCastException ex) {
+                    // The TargetInsn / CodeAddress invariant was violated.
+                    throw new IllegalStateException("unpaired TargetInsn");
+                }
+                TargetInsn gotoInsn =
+                    new TargetInsn(Dops.GOTO, target.getPosition(),
+                            RegisterSpecList.EMPTY, target.getTarget());
+                insns.set(i, gotoInsn);
+                insns.add(i, target.withNewTargetAndReversed(newTarget));
+                size++;
+                i++;
+            }
+
+            anyFixed = true;
+        }
+
+        return anyFixed;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/PositionList.java b/dx/src/com/android/dx/dex/code/PositionList.java
new file mode 100644
index 0000000..d8f76eb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/PositionList.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of source position entries. This class includes a utility
+ * method to extract an instance out of a {@link DalvInsnList}.
+ */
+public final class PositionList extends FixedSizeList {
+    /** non-null; empty instance */
+    public static final PositionList EMPTY = new PositionList(0);
+
+    /**
+     * constant for {@link #make} to indicate that no actual position
+     * information should be returned 
+     */
+    public static final int NONE = 1;
+
+    /**
+     * constant for {@link #make} to indicate that only line number
+     * transitions should be returned 
+     */
+    public static final int LINES = 2;
+
+    /**
+     * constant for {@link #make} to indicate that only "important" position
+     * information should be returned. This includes block starts and
+     * instructions that might throw. 
+     */
+    public static final int IMPORTANT = 3;
+
+    /**
+     * Extracts and returns the source position information out of an
+     * instruction list.
+     * 
+     * @param insns non-null; instructions to convert
+     * @param howMuch how much information should be included; one of the
+     * static constants defined by this class
+     * @return non-null; the positions list
+     */
+    public static PositionList make(DalvInsnList insns, int howMuch) {
+        switch (howMuch) {
+            case NONE: {
+                return EMPTY;
+            }
+            case LINES:
+            case IMPORTANT: {
+                // Valid.
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("bogus howMuch");
+            }
+        }
+
+        SourcePosition noInfo = SourcePosition.NO_INFO;
+        SourcePosition cur = noInfo;
+        int sz = insns.size();
+        PositionList.Entry[] arr = new PositionList.Entry[sz];
+        boolean lastWasTarget = false;
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = insns.get(i);
+
+            if (insn instanceof CodeAddress) {
+                lastWasTarget = true;;
+                continue;
+            }
+
+            SourcePosition pos = insn.getPosition();
+
+            if (pos.equals(noInfo) || pos.sameLine(cur)) {
+                continue;
+            }
+
+            if ((howMuch == IMPORTANT) && !lastWasTarget) {
+                continue;
+            }
+
+            cur = pos;
+            arr[at] = new PositionList.Entry(insn.getAddress(), pos);
+            at++;
+
+            lastWasTarget = false;
+        }
+
+        PositionList result = new PositionList(at);
+        for (int i = 0; i < at; i++) {
+            result.set(i, arr[i]);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size &gt;= 0; the size of the list
+     */
+    public PositionList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param entry non-null; the entry to set at <code>n</code>
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /**
+     * Entry in a position list.
+     */
+    public static class Entry {
+        /** &gt;= 0; address of this entry */
+        private final int address;
+
+        /** non-null; corresponding source position information */
+        private final SourcePosition position;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param address &gt;= 0; address of this entry
+         * @param position non-null; corresponding source position information
+         */
+        public Entry (int address, SourcePosition position) {
+            if (address < 0) {
+                throw new IllegalArgumentException("address < 0");
+            }
+
+            if (position == null) {
+                throw new NullPointerException("position == null");
+            }
+
+            this.address = address;
+            this.position = position;
+        }
+
+        /**
+         * Gets the address.
+         * 
+         * @return &gt;= 0; the address
+         */
+        public int getAddress() {
+            return address;
+        }
+
+        /**
+         * Gets the source position information.
+         * 
+         * @return non-null; the position information
+         */
+        public SourcePosition getPosition() {
+            return position;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java
new file mode 100644
index 0000000..8ad0e64
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Translator from rop-level {@link Insn} instances to corresponding
+ * {@link Dop} instances.
+ */
+public final class RopToDop {
+    /** non-null; map from all the common rops to dalvik opcodes */
+    private static final HashMap<Rop, Dop> MAP;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private RopToDop() {
+        // This space intentionally left blank.
+    }
+
+    static {
+        /*
+         * Note: The choices made here are to pick the optimistically
+         * smallest Dalvik opcode, and leave it to later processing to
+         * pessimize.
+         */
+        MAP = new HashMap<Rop, Dop>(400);
+        MAP.put(Rops.NOP,               Dops.NOP);
+        MAP.put(Rops.MOVE_INT,          Dops.MOVE);
+        MAP.put(Rops.MOVE_LONG,         Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_FLOAT,        Dops.MOVE);
+        MAP.put(Rops.MOVE_DOUBLE,       Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_OBJECT,       Dops.MOVE_OBJECT);
+        MAP.put(Rops.MOVE_PARAM_INT,    Dops.MOVE);
+        MAP.put(Rops.MOVE_PARAM_LONG,   Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_PARAM_FLOAT,  Dops.MOVE);
+        MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT);
+
+        /*
+         * Note: No entry for MOVE_EXCEPTION, since it varies by
+         * exception type. (That is, there is no unique instance to
+         * add to the map.)
+         */
+
+        MAP.put(Rops.CONST_INT,         Dops.CONST_4);
+        MAP.put(Rops.CONST_LONG,        Dops.CONST_WIDE_16);
+        MAP.put(Rops.CONST_FLOAT,       Dops.CONST_4);
+        MAP.put(Rops.CONST_DOUBLE,      Dops.CONST_WIDE_16);
+
+        /*
+         * Note: No entry for CONST_OBJECT, since it needs to turn
+         * into either CONST_STRING or CONST_CLASS.
+         */
+
+        /*
+         * TODO: I think the only case of this is for null, and
+         * const/4 should cover that.
+         */
+        MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4);
+
+        MAP.put(Rops.GOTO,                 Dops.GOTO);
+        MAP.put(Rops.IF_EQZ_INT,           Dops.IF_EQZ);
+        MAP.put(Rops.IF_NEZ_INT,           Dops.IF_NEZ);
+        MAP.put(Rops.IF_LTZ_INT,           Dops.IF_LTZ);
+        MAP.put(Rops.IF_GEZ_INT,           Dops.IF_GEZ);
+        MAP.put(Rops.IF_LEZ_INT,           Dops.IF_LEZ);
+        MAP.put(Rops.IF_GTZ_INT,           Dops.IF_GTZ);
+        MAP.put(Rops.IF_EQZ_OBJECT,        Dops.IF_EQZ);
+        MAP.put(Rops.IF_NEZ_OBJECT,        Dops.IF_NEZ);
+        MAP.put(Rops.IF_EQ_INT,            Dops.IF_EQ);
+        MAP.put(Rops.IF_NE_INT,            Dops.IF_NE);
+        MAP.put(Rops.IF_LT_INT,            Dops.IF_LT);
+        MAP.put(Rops.IF_GE_INT,            Dops.IF_GE);
+        MAP.put(Rops.IF_LE_INT,            Dops.IF_LE);
+        MAP.put(Rops.IF_GT_INT,            Dops.IF_GT);
+        MAP.put(Rops.IF_EQ_OBJECT,         Dops.IF_EQ);
+        MAP.put(Rops.IF_NE_OBJECT,         Dops.IF_NE);
+        MAP.put(Rops.SWITCH,               Dops.SPARSE_SWITCH);
+        MAP.put(Rops.ADD_INT,              Dops.ADD_INT_2ADDR);
+        MAP.put(Rops.ADD_LONG,             Dops.ADD_LONG_2ADDR);
+        MAP.put(Rops.ADD_FLOAT,            Dops.ADD_FLOAT_2ADDR);
+        MAP.put(Rops.ADD_DOUBLE,           Dops.ADD_DOUBLE_2ADDR);
+        MAP.put(Rops.SUB_INT,              Dops.SUB_INT_2ADDR);
+        MAP.put(Rops.SUB_LONG,             Dops.SUB_LONG_2ADDR);
+        MAP.put(Rops.SUB_FLOAT,            Dops.SUB_FLOAT_2ADDR);
+        MAP.put(Rops.SUB_DOUBLE,           Dops.SUB_DOUBLE_2ADDR);
+        MAP.put(Rops.MUL_INT,              Dops.MUL_INT_2ADDR);
+        MAP.put(Rops.MUL_LONG,             Dops.MUL_LONG_2ADDR);
+        MAP.put(Rops.MUL_FLOAT,            Dops.MUL_FLOAT_2ADDR);
+        MAP.put(Rops.MUL_DOUBLE,           Dops.MUL_DOUBLE_2ADDR);
+        MAP.put(Rops.DIV_INT,              Dops.DIV_INT_2ADDR);
+        MAP.put(Rops.DIV_LONG,             Dops.DIV_LONG_2ADDR);
+        MAP.put(Rops.DIV_FLOAT,            Dops.DIV_FLOAT_2ADDR);
+        MAP.put(Rops.DIV_DOUBLE,           Dops.DIV_DOUBLE_2ADDR);
+        MAP.put(Rops.REM_INT,              Dops.REM_INT_2ADDR);
+        MAP.put(Rops.REM_LONG,             Dops.REM_LONG_2ADDR);
+        MAP.put(Rops.REM_FLOAT,            Dops.REM_FLOAT_2ADDR);
+        MAP.put(Rops.REM_DOUBLE,           Dops.REM_DOUBLE_2ADDR);
+        MAP.put(Rops.NEG_INT,              Dops.NEG_INT);
+        MAP.put(Rops.NEG_LONG,             Dops.NEG_LONG);
+        MAP.put(Rops.NEG_FLOAT,            Dops.NEG_FLOAT);
+        MAP.put(Rops.NEG_DOUBLE,           Dops.NEG_DOUBLE);
+        MAP.put(Rops.AND_INT,              Dops.AND_INT_2ADDR);
+        MAP.put(Rops.AND_LONG,             Dops.AND_LONG_2ADDR);
+        MAP.put(Rops.OR_INT,               Dops.OR_INT_2ADDR);
+        MAP.put(Rops.OR_LONG,              Dops.OR_LONG_2ADDR);
+        MAP.put(Rops.XOR_INT,              Dops.XOR_INT_2ADDR);
+        MAP.put(Rops.XOR_LONG,             Dops.XOR_LONG_2ADDR);
+        MAP.put(Rops.SHL_INT,              Dops.SHL_INT_2ADDR);
+        MAP.put(Rops.SHL_LONG,             Dops.SHL_LONG_2ADDR);
+        MAP.put(Rops.SHR_INT,              Dops.SHR_INT_2ADDR);
+        MAP.put(Rops.SHR_LONG,             Dops.SHR_LONG_2ADDR);
+        MAP.put(Rops.USHR_INT,             Dops.USHR_INT_2ADDR);
+        MAP.put(Rops.USHR_LONG,            Dops.USHR_LONG_2ADDR);
+        MAP.put(Rops.NOT_INT,              Dops.NOT_INT);
+        MAP.put(Rops.NOT_LONG,             Dops.NOT_LONG);
+
+        MAP.put(Rops.ADD_CONST_INT,        Dops.ADD_INT_LIT8);
+        // Note: No dalvik ops for other types of add_const.
+
+        /*
+         * Note: No dalvik ops for any type of sub_const; there's a
+         * *reverse* sub (constant - reg) for ints, though, but that
+         * should end up getting handled at optimization time.
+         */
+
+        MAP.put(Rops.MUL_CONST_INT,        Dops.MUL_INT_LIT8);
+        // Note: No dalvik ops for other types of mul_const.
+
+        MAP.put(Rops.DIV_CONST_INT,        Dops.DIV_INT_LIT8);
+        // Note: No dalvik ops for other types of div_const.
+
+        MAP.put(Rops.REM_CONST_INT,        Dops.REM_INT_LIT8);
+        // Note: No dalvik ops for other types of rem_const.
+
+        MAP.put(Rops.AND_CONST_INT,        Dops.AND_INT_LIT8);
+        // Note: No dalvik op for and_const_long.
+
+        MAP.put(Rops.OR_CONST_INT,         Dops.OR_INT_LIT8);
+        // Note: No dalvik op for or_const_long.
+
+        MAP.put(Rops.XOR_CONST_INT,        Dops.XOR_INT_LIT8);
+        // Note: No dalvik op for xor_const_long.
+
+        MAP.put(Rops.SHL_CONST_INT,        Dops.SHL_INT_LIT8);
+        // Note: No dalvik op for shl_const_long.
+
+        MAP.put(Rops.SHR_CONST_INT,        Dops.SHR_INT_LIT8);
+        // Note: No dalvik op for shr_const_long.
+
+        MAP.put(Rops.USHR_CONST_INT,       Dops.USHR_INT_LIT8);
+        // Note: No dalvik op for shr_const_long.
+
+        MAP.put(Rops.CMPL_LONG,            Dops.CMP_LONG);
+        MAP.put(Rops.CMPL_FLOAT,           Dops.CMPL_FLOAT);
+        MAP.put(Rops.CMPL_DOUBLE,          Dops.CMPL_DOUBLE);
+        MAP.put(Rops.CMPG_FLOAT,           Dops.CMPG_FLOAT);
+        MAP.put(Rops.CMPG_DOUBLE,          Dops.CMPG_DOUBLE);
+        MAP.put(Rops.CONV_L2I,             Dops.LONG_TO_INT);
+        MAP.put(Rops.CONV_F2I,             Dops.FLOAT_TO_INT);
+        MAP.put(Rops.CONV_D2I,             Dops.DOUBLE_TO_INT);
+        MAP.put(Rops.CONV_I2L,             Dops.INT_TO_LONG);
+        MAP.put(Rops.CONV_F2L,             Dops.FLOAT_TO_LONG);
+        MAP.put(Rops.CONV_D2L,             Dops.DOUBLE_TO_LONG);
+        MAP.put(Rops.CONV_I2F,             Dops.INT_TO_FLOAT);
+        MAP.put(Rops.CONV_L2F,             Dops.LONG_TO_FLOAT);
+        MAP.put(Rops.CONV_D2F,             Dops.DOUBLE_TO_FLOAT);
+        MAP.put(Rops.CONV_I2D,             Dops.INT_TO_DOUBLE);
+        MAP.put(Rops.CONV_L2D,             Dops.LONG_TO_DOUBLE);
+        MAP.put(Rops.CONV_F2D,             Dops.FLOAT_TO_DOUBLE);
+        MAP.put(Rops.TO_BYTE,              Dops.INT_TO_BYTE);
+        MAP.put(Rops.TO_CHAR,              Dops.INT_TO_CHAR);
+        MAP.put(Rops.TO_SHORT,             Dops.INT_TO_SHORT);
+        MAP.put(Rops.RETURN_VOID,          Dops.RETURN_VOID);
+        MAP.put(Rops.RETURN_INT,           Dops.RETURN);
+        MAP.put(Rops.RETURN_LONG,          Dops.RETURN_WIDE);
+        MAP.put(Rops.RETURN_FLOAT,         Dops.RETURN);
+        MAP.put(Rops.RETURN_DOUBLE,        Dops.RETURN_WIDE);
+        MAP.put(Rops.RETURN_OBJECT,        Dops.RETURN_OBJECT);
+        MAP.put(Rops.ARRAY_LENGTH,         Dops.ARRAY_LENGTH);
+        MAP.put(Rops.THROW,                Dops.THROW);
+        MAP.put(Rops.MONITOR_ENTER,        Dops.MONITOR_ENTER);
+        MAP.put(Rops.MONITOR_EXIT,         Dops.MONITOR_EXIT);
+        MAP.put(Rops.AGET_INT,             Dops.AGET);
+        MAP.put(Rops.AGET_LONG,            Dops.AGET_WIDE);
+        MAP.put(Rops.AGET_FLOAT,           Dops.AGET);
+        MAP.put(Rops.AGET_DOUBLE,          Dops.AGET_WIDE);
+        MAP.put(Rops.AGET_OBJECT,          Dops.AGET_OBJECT);
+        MAP.put(Rops.AGET_BOOLEAN,         Dops.AGET_BOOLEAN);
+        MAP.put(Rops.AGET_BYTE,            Dops.AGET_BYTE);
+        MAP.put(Rops.AGET_CHAR,            Dops.AGET_CHAR);
+        MAP.put(Rops.AGET_SHORT,           Dops.AGET_SHORT);
+        MAP.put(Rops.APUT_INT,             Dops.APUT);
+        MAP.put(Rops.APUT_LONG,            Dops.APUT_WIDE);
+        MAP.put(Rops.APUT_FLOAT,           Dops.APUT);
+        MAP.put(Rops.APUT_DOUBLE,          Dops.APUT_WIDE);
+        MAP.put(Rops.APUT_OBJECT,          Dops.APUT_OBJECT);
+        MAP.put(Rops.APUT_BOOLEAN,         Dops.APUT_BOOLEAN);
+        MAP.put(Rops.APUT_BYTE,            Dops.APUT_BYTE);
+        MAP.put(Rops.APUT_CHAR,            Dops.APUT_CHAR);
+        MAP.put(Rops.APUT_SHORT,           Dops.APUT_SHORT);
+        MAP.put(Rops.NEW_INSTANCE,         Dops.NEW_INSTANCE);
+        MAP.put(Rops.CHECK_CAST,           Dops.CHECK_CAST);
+        MAP.put(Rops.INSTANCE_OF,          Dops.INSTANCE_OF);
+
+        MAP.put(Rops.GET_FIELD_LONG,       Dops.IGET_WIDE);
+        MAP.put(Rops.GET_FIELD_FLOAT,      Dops.IGET);
+        MAP.put(Rops.GET_FIELD_DOUBLE,     Dops.IGET_WIDE);
+        MAP.put(Rops.GET_FIELD_OBJECT,     Dops.IGET_OBJECT);
+        /*
+         * Note: No map entries for get_field_* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.GET_STATIC_LONG,      Dops.SGET_WIDE);
+        MAP.put(Rops.GET_STATIC_FLOAT,     Dops.SGET);
+        MAP.put(Rops.GET_STATIC_DOUBLE,    Dops.SGET_WIDE);
+        MAP.put(Rops.GET_STATIC_OBJECT,    Dops.SGET_OBJECT);
+        /*
+         * Note: No map entries for get_static* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.PUT_FIELD_LONG,       Dops.IPUT_WIDE);
+        MAP.put(Rops.PUT_FIELD_FLOAT,      Dops.IPUT);
+        MAP.put(Rops.PUT_FIELD_DOUBLE,     Dops.IPUT_WIDE);
+        MAP.put(Rops.PUT_FIELD_OBJECT,     Dops.IPUT_OBJECT);
+        /*
+         * Note: No map entries for put_field_* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.PUT_STATIC_LONG,      Dops.SPUT_WIDE);
+        MAP.put(Rops.PUT_STATIC_FLOAT,     Dops.SPUT);
+        MAP.put(Rops.PUT_STATIC_DOUBLE,    Dops.SPUT_WIDE);
+        MAP.put(Rops.PUT_STATIC_OBJECT,    Dops.SPUT_OBJECT);
+        /*
+         * Note: No map entries for put_static* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        /*
+         * Note: No map entries for invoke*, new_array, and
+         * filled_new_array, since they need to be handled specially
+         * (see dopFor() below).
+         */
+    }
+
+    /**
+     * Returns the dalvik opcode appropriate for the given register-based
+     * instruction.
+     * 
+     * @param insn non-null; the original instruction
+     * @return the corresponding dalvik opcode; one of the constants in
+     * {@link Dops}
+     */
+    public static Dop dopFor(Insn insn) {
+        Rop rop = insn.getOpcode();
+
+        /*
+         * First, just try looking up the rop in the MAP of easy
+         * cases.
+         */
+        Dop result = MAP.get(rop);
+        if (result != null) {
+            return result;
+        }
+
+        /*
+         * There was no easy case for the rop, so look up the opcode, and
+         * do something special for each:
+         * 
+         * The move_exception, new_array, filled_new_array, and
+         * invoke* opcodes won't be found in MAP, since they'll each
+         * have different source and/or result register types / lists.
+         * 
+         * The get* and put* opcodes for (non-long) integral types
+         * aren't in the map, since the type signatures aren't
+         * sufficient to distinguish between the types (the salient
+         * source or result will always be just "int").
+         * 
+         * And const instruction need to distinguish between strings and
+         * classes.
+         */
+
+        switch (rop.getOpcode()) {
+            case RegOps.MOVE_EXCEPTION:   return Dops.MOVE_EXCEPTION;
+            case RegOps.INVOKE_STATIC:    return Dops.INVOKE_STATIC;
+            case RegOps.INVOKE_VIRTUAL:   return Dops.INVOKE_VIRTUAL;
+            case RegOps.INVOKE_SUPER:     return Dops.INVOKE_SUPER;
+            case RegOps.INVOKE_DIRECT:    return Dops.INVOKE_DIRECT;
+            case RegOps.INVOKE_INTERFACE: return Dops.INVOKE_INTERFACE;
+            case RegOps.NEW_ARRAY:        return Dops.NEW_ARRAY;
+            case RegOps.FILLED_NEW_ARRAY: return Dops.FILLED_NEW_ARRAY;
+            case RegOps.FILL_ARRAY_DATA:  return Dops.FILL_ARRAY_DATA; 
+            case RegOps.MOVE_RESULT: {
+                RegisterSpec resultReg = insn.getResult();
+
+                if (resultReg == null) {
+                    return Dops.NOP;
+                } else {
+                    switch (resultReg.getBasicType()) {
+                        case Type.BT_INT:
+                        case Type.BT_FLOAT:
+                        case Type.BT_BOOLEAN:
+                        case Type.BT_BYTE:
+                        case Type.BT_CHAR:
+                        case Type.BT_SHORT:
+                            return Dops.MOVE_RESULT;
+                        case Type.BT_LONG:
+                        case Type.BT_DOUBLE:
+                            return Dops.MOVE_RESULT_WIDE;
+                        case Type.BT_OBJECT:
+                            return Dops.MOVE_RESULT_OBJECT;
+                        default: {
+                            throw new RuntimeException("Unexpected basic type");
+                        }
+                    }
+                }
+            }
+
+            case RegOps.GET_FIELD: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.IGET_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.IGET_BYTE;
+                    case Type.BT_CHAR:    return Dops.IGET_CHAR;
+                    case Type.BT_SHORT:   return Dops.IGET_SHORT;
+                    case Type.BT_INT:     return Dops.IGET;
+                }
+                break;
+            }
+            case RegOps.PUT_FIELD: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.IPUT_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.IPUT_BYTE;
+                    case Type.BT_CHAR:    return Dops.IPUT_CHAR;
+                    case Type.BT_SHORT:   return Dops.IPUT_SHORT;
+                    case Type.BT_INT:     return Dops.IPUT;
+                }
+                break;
+            }
+            case RegOps.GET_STATIC: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.SGET_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.SGET_BYTE;
+                    case Type.BT_CHAR:    return Dops.SGET_CHAR;
+                    case Type.BT_SHORT:   return Dops.SGET_SHORT;
+                    case Type.BT_INT:     return Dops.SGET;
+                }
+                break;
+            }
+            case RegOps.PUT_STATIC: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.SPUT_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.SPUT_BYTE;
+                    case Type.BT_CHAR:    return Dops.SPUT_CHAR;
+                    case Type.BT_SHORT:   return Dops.SPUT_SHORT;
+                    case Type.BT_INT:     return Dops.SPUT;
+                }
+                break;
+            }
+            case RegOps.CONST: {
+                Constant cst = ((ThrowingCstInsn) insn).getConstant();
+                if (cst instanceof CstType) {
+                    return Dops.CONST_CLASS;
+                } else if (cst instanceof CstString) {
+                    return Dops.CONST_STRING;
+                }
+                break;
+            }
+        }
+
+        throw new RuntimeException("unknown rop: " + rop);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopTranslator.java b/dx/src/com/android/dx/dex/code/RopTranslator.java
new file mode 100644
index 0000000..6a4b888
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopTranslator.java
@@ -0,0 +1,869 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Translator from {@link RopMethod} to {@link DalvCode}. The {@link
+ * #translate} method is the thing to call on this class.
+ */
+public final class RopTranslator {
+    /** non-null; method to translate */
+    private final RopMethod method;
+
+    /**
+     * how much position info to preserve; one of the static
+     * constants in {@link PositionList} 
+     */
+    private final int positionInfo;
+
+    /** null-ok; local variable info to use */
+    private final LocalVariableInfo locals;
+
+    /** non-null; container for all the address objects for the method */
+    private final BlockAddresses addresses;
+
+    /** non-null; list of output instructions in-progress */
+    private final OutputCollector output;
+
+    /** non-null; visitor to use during translation */
+    private final TranslationVisitor translationVisitor;
+
+    /** &gt;= 0; register count for the method */
+    private final int regCount;
+
+    /** null-ok; block output order; becomes non-null in {@link #pickOrder} */
+    private int[] order;
+
+    /** size, in register units, of all the parameters to this method */
+    private final int paramSize;
+
+    /**
+     * true if the parameters to this method happen to be in proper order
+     * at the end of the frame (as the optimizer emits them)
+     */
+    private boolean paramsAreInOrder;
+
+    /**
+     * Translates a {@link RopMethod}. This may modify the given
+     * input.
+     * 
+     * @param method non-null; the original method
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param locals null-ok; local variable information to use
+     * @param paramSize size, in register units, of all the parameters to
+     * this method
+     * @return non-null; the translated version
+     */
+    public static DalvCode translate(RopMethod method, int positionInfo,
+                                     LocalVariableInfo locals, int paramSize) {
+        RopTranslator translator =
+            new RopTranslator(method, positionInfo, locals,
+                    paramSize);
+        return translator.translateAndGetResult();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #translate}.
+     * 
+     * @param method non-null; the original method
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param locals null-ok; local variable information to use
+     * @param paramSize size, in register units, of all the parameters to
+     * this method
+     */
+    private RopTranslator(RopMethod method, int positionInfo,
+                          LocalVariableInfo locals, int paramSize) {
+        this.method = method;
+        this.positionInfo = positionInfo;
+        this.locals = locals;
+        this.addresses = new BlockAddresses(method);
+        this.paramSize = paramSize;
+        this.order = null;
+        this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize);
+
+        BasicBlockList blocks = method.getBlocks();
+        int bsz = blocks.size();
+
+        /*
+         * Max possible instructions includes three code address
+         * objects per basic block (to the first and last instruction,
+         * and just past the end of the block), and the possibility of
+         * an extra goto at the end of each basic block.
+         */
+        int maxInsns = (bsz * 3) + blocks.getInstructionCount();
+
+        if (locals != null) {
+            /*
+             * If we're tracking locals, then there's could be another
+             * extra instruction per block (for the locals state at the
+             * start of the block) as well as one for each interblock
+             * local introduction.
+             */
+            maxInsns += bsz + locals.getAssignmentCount();
+        }
+
+        /*
+         * If params are not in order, we will need register space
+         * for them before this is all over...
+         */
+        this.regCount = blocks.getRegCount()
+                + (paramsAreInOrder ? 0 : this.paramSize);
+
+        this.output = new OutputCollector(maxInsns, bsz * 3, regCount);
+
+        if (locals != null) {
+            this.translationVisitor =
+                new LocalVariableAwareTranslationVisitor(output, locals);
+        } else {
+            this.translationVisitor = new TranslationVisitor(output);
+        }
+    }
+
+    /**
+     * Checks to see if the move-param instructions that occur in this
+     * method happen to slot the params in an order at the top of the
+     * stack frame that matches dalvik's calling conventions. This will
+     * alway result in "true" for methods that have run through the
+     * SSA optimizer.
+     * 
+     * @param paramSize size, in register units, of all the parameters
+     * to this method
+     */
+    private static boolean calculateParamsAreInOrder(RopMethod method,
+            final int paramSize) {
+        final boolean[] paramsAreInOrder = { true };
+        final int initialRegCount = method.getBlocks().getRegCount();
+
+        /*
+         * We almost could just check the first block here, but the
+         * <code>cf</code> layer will put in a second move-param in a
+         * subsequent block in the case of synchronized methods.
+         */
+        method.getBlocks().forEachInsn(new Insn.BaseVisitor() {
+            public void visitPlainCstInsn(PlainCstInsn insn) {
+                if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) {
+                    int param =
+                        ((CstInteger) insn.getConstant()).getValue();
+
+                    paramsAreInOrder[0] = paramsAreInOrder[0]
+                            && ((initialRegCount - paramSize + param)
+                                == insn.getResult().getReg());
+                }
+            }
+        });
+
+        return paramsAreInOrder[0];
+    }
+
+    /**
+     * Does the translation and returns the result.
+     * 
+     * @return non-null; the result
+     */
+    private DalvCode translateAndGetResult() {
+        pickOrder();
+        outputInstructions();
+
+        CatchBuilder catches = new CatchBuilder(method, order, addresses);
+
+        return new DalvCode(positionInfo, output.getFinisher(), catches);
+    }
+
+    /**
+     * Performs initial creation of output instructions based on the
+     * original blocks.
+     */
+    private void outputInstructions() {
+        BasicBlockList blocks = method.getBlocks();
+        int[] order = this.order;
+        int len = order.length;
+
+        // Process the blocks in output order.
+        for (int i = 0; i < len; i++) {
+            int nextI = i + 1;
+            int nextLabel = (nextI == order.length) ? -1 : order[nextI];
+            outputBlock(blocks.labelToBlock(order[i]), nextLabel);
+        }
+    }
+
+    /**
+     * Helper for {@link #outputInstructions}, which does the processing
+     * and output of one block.
+     * 
+     * @param block non-null; the block to process and output
+     * @param nextLabel &gt;= -1; the next block that will be processed, or
+     * <code>-1</code> if there is no next block
+     */
+    private void outputBlock(BasicBlock block, int nextLabel) {
+        // Append the code address for this block.
+        CodeAddress startAddress = addresses.getStart(block);
+        output.add(startAddress);
+
+        // Append the local variable state for the block.
+        if (locals != null) {
+            RegisterSpecSet starts = locals.getStarts(block);
+            output.add(new LocalSnapshot(startAddress.getPosition(),
+                                         starts));
+        }
+
+        /*
+         * Choose and append an output instruction for each original
+         * instruction.
+         */
+        translationVisitor.setBlock(block, addresses.getLast(block));
+        block.getInsns().forEach(translationVisitor);
+
+        // Insert the block end code address.
+        output.add(addresses.getEnd(block));
+
+        // Set up for end-of-block activities. 
+
+        int succ = block.getPrimarySuccessor();
+        Insn lastInsn = block.getLastInsn();
+
+        /*
+         * Check for (and possibly correct for) a non-optimal choice of
+         * which block will get output next.
+         */
+
+        if ((succ >= 0) && (succ != nextLabel)) {
+            /*
+             * The block has a "primary successor" and that primary
+             * successor isn't the next block to be output.
+             */
+            Rop lastRop = lastInsn.getOpcode();
+            if ((lastRop.getBranchingness() == Rop.BRANCH_IF) &&
+                    (block.getSecondarySuccessor() == nextLabel)) {
+                /*
+                 * The block ends with an "if" of some sort, and its
+                 * secondary successor (the "then") is in fact the
+                 * next block to output. So, reverse the sense of
+                 * the test, so that we can just emit the next block
+                 * without an interstitial goto.
+                 */
+                output.reverseBranch(1, addresses.getStart(succ));
+            } else {
+                /*
+                 * Our only recourse is to add a goto here to get the
+                 * flow to be correct.
+                 */
+                TargetInsn insn =
+                    new TargetInsn(Dops.GOTO, lastInsn.getPosition(),
+                            RegisterSpecList.EMPTY,
+                            addresses.getStart(succ));
+                output.add(insn);
+            }
+        }
+    }
+
+    /**
+     * Picks an order for the blocks by doing "trace" analysis.
+     */
+    private void pickOrder() {
+        BasicBlockList blocks = method.getBlocks();
+        int sz = blocks.size();
+        int maxLabel = blocks.getMaxLabel();
+        int[] workSet = Bits.makeBitSet(maxLabel);
+        int[] tracebackSet = Bits.makeBitSet(maxLabel);
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            Bits.set(workSet, one.getLabel());
+        }
+
+        int[] order = new int[sz];
+        int at = 0;
+
+        /*
+         * Starting with the designated "first label" (that is, the
+         * first block of the method), add that label to the order,
+         * and then pick its first as-yet unordered successor to
+         * immediately follow it, giving top priority to the primary
+         * (aka default) successor (if any). Keep following successors
+         * until the trace runs out of possibilities. Then, continue
+         * by finding an unordered chain containing the first as-yet
+         * unordered block, and adding it to the order, and so on.
+         */
+        for (int label = method.getFirstLabel();
+             label != -1;
+             label = Bits.findFirst(workSet, 0)) {
+
+            /*
+             * Attempt to trace backward from the chosen block to an
+             * as-yet unordered predecessor which lists the chosen
+             * block as its primary successor, and so on, until we
+             * fail to find such an unordered predecessor. Start the
+             * trace with that block. Note that the first block in the
+             * method has no predecessors, so in that case this loop
+             * will simply terminate with zero iterations and without
+             * picking a new starter block.
+             */
+            traceBack:
+            for (;;) {
+                IntList preds = method.labelToPredecessors(label);
+                int psz = preds.size();
+
+                for (int i = 0; i < psz; i++) {
+                    int predLabel = preds.get(i);
+
+                    if (Bits.get(tracebackSet, predLabel)) {
+                        /*
+                         * We found a predecessor loop; stop tracing back
+                         * from here.
+                         */
+                        break;
+                    }
+
+                    if (!Bits.get(workSet, predLabel)) {
+                        // This one's already ordered.
+                        continue;
+                    }
+
+                    BasicBlock pred = blocks.labelToBlock(predLabel);
+                    if (pred.getPrimarySuccessor() == label) {
+                        // Found one!
+                        label = predLabel;
+                        Bits.set(tracebackSet, label);
+                        continue traceBack;
+                    }
+                }
+
+                // Failed to find a better block to start the trace.
+                break;
+            }
+
+            /*
+             * Trace a path from the chosen block to one of its
+             * unordered successors (hopefully the primary), and so
+             * on, until we run out of unordered successors.
+             */
+            while (label != -1) {
+                Bits.clear(workSet, label);
+                Bits.clear(tracebackSet, label);
+                order[at] = label;
+                at++;
+
+                BasicBlock one = blocks.labelToBlock(label);
+                BasicBlock preferredBlock = blocks.preferredSuccessorOf(one);
+
+                if (preferredBlock == null) {
+                    break;
+                }
+                
+                int preferred = preferredBlock.getLabel();
+                int primary = one.getPrimarySuccessor();
+
+                if (Bits.get(workSet, preferred)) {
+                    /*
+                     * Order the current block's preferred successor
+                     * next, as it has yet to be scheduled.
+                     */
+                    label = preferred;
+                } else if ((primary != preferred) && (primary >= 0)
+                        && Bits.get(workSet, primary)) {
+                    /*
+                     * The primary is available, so use that.
+                     */
+                    label = primary;
+                } else {
+                    /*
+                     * There's no obvious candidate, so pick the first
+                     * one that's available, if any.
+                     */
+                    IntList successors = one.getSuccessors();
+                    int ssz = successors.size();
+                    label = -1;
+                    for (int i = 0; i < ssz; i++) {
+                        int candidate = successors.get(i);
+                        if (Bits.get(workSet, candidate)) {
+                            label = candidate;
+                            break;
+                        }
+                    }
+                }
+            }
+        }        
+
+        if (at != sz) {
+            // There was a duplicate block label.
+            throw new RuntimeException("shouldn't happen");
+        }
+
+        this.order = order;
+    }
+
+    /**
+     * Gets the complete register list (result and sources) out of a
+     * given rop instruction. For insns that are commutative, have
+     * two register sources, and have a source equal to the result,
+     * place that source first.
+     *
+     * @param insn non-null; instruction in question
+     * @return non-null; the instruction's complete register list
+     */
+    private static RegisterSpecList getRegs(Insn insn) {
+        return getRegs(insn, insn.getResult());
+    }
+
+    /**
+     * Gets the complete register list (result and sources) out of a
+     * given rop instruction. For insns that are commutative, have
+     * two register sources, and have a source equal to the result,
+     * place that source first.
+     *
+     * @param insn non-null; instruction in question
+     * @param resultReg null-ok; the real result to use (ignore the insn's)
+     * @return non-null; the instruction's complete register list
+     */
+    private static RegisterSpecList getRegs(Insn insn, RegisterSpec resultReg) {
+        RegisterSpecList regs = insn.getSources();
+
+        if (insn.getOpcode().isCommutative()
+                && (regs.size() == 2)
+                && (resultReg.getReg() == regs.get(1).getReg())) {
+
+            /*
+             * For commutative ops which have two register sources,
+             * if the second source is the same register as the result,
+             * swap the sources so that an opcode of form 12x can be selected
+             * instead of one of form 23x
+             */
+
+            regs = RegisterSpecList.make(regs.get(1), regs.get(0));
+        }
+
+        if (resultReg == null) {
+            return regs;
+        }
+
+        return regs.withFirst(resultReg);
+    }
+
+    /**
+     * Instruction visitor class for doing the instruction translation per se.
+     */
+    private class TranslationVisitor implements Insn.Visitor {
+        /** non-null; list of output instructions in-progress */
+        private final OutputCollector output;
+
+        /** non-null; basic block being worked on */
+        private BasicBlock block;
+
+        /**
+         * null-ok; code address for the salient last instruction of the
+         * block (used before switches and throwing instructions) 
+         */
+        private CodeAddress lastAddress;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param output non-null; destination for instruction output
+         */
+        public TranslationVisitor(OutputCollector output) {
+            this.output = output;
+        }
+
+        /**
+         * Sets the block currently being worked on.
+         * 
+         * @param block non-null; the block
+         * @param lastAddress non-null; code address for the salient
+         * last instruction of the block
+         */
+        public void setBlock(BasicBlock block, CodeAddress lastAddress) {
+            this.block = block;
+            this.lastAddress = lastAddress;
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            Rop rop = insn.getOpcode();
+            if (rop.getOpcode() == RegOps.MARK_LOCAL) {
+                /*
+                 * Ignore these. They're dealt with by
+                 * the LocalVariableAwareTranslationVisitor
+                 */
+                return;
+            }
+            if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+                // These get skipped
+                return;
+            }
+
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            DalvInsn di;
+
+            switch (rop.getBranchingness()) {
+                case Rop.BRANCH_NONE:
+                case Rop.BRANCH_RETURN:
+                case Rop.BRANCH_THROW: {
+                    di = new SimpleInsn(opcode, pos, getRegs(insn));
+                    break;
+                }
+                case Rop.BRANCH_GOTO: {
+                    /*
+                     * Code in the main translation loop will emit a
+                     * goto if necessary (if the branch isn't to the
+                     * immediately subsequent block).
+                     */
+                    return;
+                }
+                case Rop.BRANCH_IF: {
+                    int target = block.getSuccessors().get(1);
+                    di = new TargetInsn(opcode, pos, getRegs(insn),
+                                        addresses.getStart(target));
+                    break;
+                }
+                default: {
+                    throw new RuntimeException("shouldn't happen");
+                }
+            }
+
+            addOutput(di);
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            int ropOpcode = rop.getOpcode();
+            DalvInsn di;
+
+            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            if (ropOpcode == RegOps.MOVE_PARAM) {
+                if (!paramsAreInOrder) {
+                    /*
+                     * Parameters are not in order at the top of the reg space.
+                     * We need to add moves.
+                     */
+
+                    RegisterSpec dest = insn.getResult();
+                    int param =
+                        ((CstInteger) insn.getConstant()).getValue();
+                    RegisterSpec source =
+                        RegisterSpec.make(regCount - paramSize + param,
+                                dest.getType());
+                    di = new SimpleInsn(opcode, pos,
+                                        RegisterSpecList.make(dest, source));
+                    addOutput(di);
+                }
+            } else {
+                // No moves required for the parameters
+                RegisterSpecList regs = getRegs(insn);
+                di = new CstInsn(opcode, pos, regs, insn.getConstant());
+                addOutput(di);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            IntList cases = insn.getCases();
+            IntList successors = block.getSuccessors();
+            int casesSz = cases.size();
+            int succSz = successors.size();
+            int primarySuccessor = block.getPrimarySuccessor();
+
+            /*
+             * Check the assumptions that the number of cases is one
+             * less than the number of successors and that the last
+             * successor in the list is the primary (in this case, the
+             * default). This test is here to guard against forgetting
+             * to change this code if the way switch instructions are
+             * constructed also gets changed.
+             */
+            if ((casesSz != (succSz - 1)) ||
+                (primarySuccessor != successors.get(casesSz))) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            CodeAddress[] switchTargets = new CodeAddress[casesSz];
+
+            for (int i = 0; i < casesSz; i++) {
+                int label = successors.get(i);
+                switchTargets[i] = addresses.getStart(label);
+            }
+
+            CodeAddress dataAddress = new CodeAddress(pos);
+            SwitchData dataInsn =
+                new SwitchData(pos, lastAddress, cases, switchTargets);
+            Dop opcode = dataInsn.isPacked() ?
+                Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
+            TargetInsn switchInsn =
+                new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
+
+            addOutput(lastAddress);
+            addOutput(switchInsn);
+
+            addOutputSuffix(new OddSpacer(pos));
+            addOutputSuffix(dataAddress);
+            addOutputSuffix(dataInsn);
+        }
+
+        /**
+         * Looks forward to the current block's primary successor, returning
+         * the RegisterSpec of the result of the move-result-pseudo at the
+         * top of that block or null if none.
+         *
+         * @return null-ok; result of move-result-pseudo at the beginning of
+         * primary successor
+         */
+        private RegisterSpec getNextMoveResultPseudo()
+        {
+            int label = block.getPrimarySuccessor();
+
+            if (label < 0) {
+                return null;
+            }
+
+            Insn insn
+                    = method.getBlocks().labelToBlock(label).getInsns().get(0);
+
+            if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
+                return null;
+            } else {
+                return insn.getResult();
+            }            
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            Constant cst = insn.getConstant();
+
+            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            addOutput(lastAddress);
+
+            if (rop.isCallLike()) {
+                RegisterSpecList regs = insn.getSources();
+                DalvInsn di = new CstInsn(opcode, pos, regs, cst);
+
+                addOutput(di);
+            } else {
+                RegisterSpec realResult = getNextMoveResultPseudo();
+
+                RegisterSpecList regs = getRegs(insn, realResult);
+                DalvInsn di;
+
+                boolean hasResult = opcode.hasResult()
+                        || (rop.getOpcode() == RegOps.CHECK_CAST);
+
+                if (hasResult != (realResult != null)) {
+                    throw new RuntimeException(
+                            "Insn with result/move-result-pseudo mismatch " + insn);
+                }
+                
+                if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
+                    (opcode.getOpcode() != DalvOps.NEW_ARRAY)) {
+                    /*
+                     * It's a type-specific new-array-<primitive>, and
+                     * so it should be turned into a SimpleInsn (no
+                     * constant ref as it's implicit).
+                     */
+                    di = new SimpleInsn(opcode, pos, regs);
+                } else {
+                    /*
+                     * This is the general case for constant-bearing
+                     * instructions.
+                     */
+                    di = new CstInsn(opcode, pos, regs, cst);
+                }
+
+                addOutput(di);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            RegisterSpec realResult;
+
+            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            realResult = getNextMoveResultPseudo();
+
+            if (opcode.hasResult() != (realResult != null)) {
+                throw new RuntimeException(
+                        "Insn with result/move-result-pseudo mismatch" + insn);
+            }
+            
+            addOutput(lastAddress);
+
+            DalvInsn di = new SimpleInsn(opcode, pos,
+                    getRegs(insn, realResult));
+
+            addOutput(di);
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Constant cst = insn.getConstant();
+            ArrayList<Constant> values = insn.getInitValues();
+            Rop rop = insn.getOpcode();
+
+            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new RuntimeException("shouldn't happen");
+            }
+            CodeAddress dataAddress = new CodeAddress(pos);
+            ArrayData dataInsn =
+                new ArrayData(pos, lastAddress, values, cst);
+            
+            TargetInsn fillArrayDataInsn =
+                new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
+                        dataAddress);
+
+            addOutput(lastAddress);
+            addOutput(fillArrayDataInsn);
+
+            addOutputSuffix(new OddSpacer(pos));
+            addOutputSuffix(dataAddress);
+            addOutputSuffix(dataInsn);
+        }
+        
+        /**
+         * Adds to the output.
+         * 
+         * @param insn non-null; instruction to add
+         */
+        protected void addOutput(DalvInsn insn) {
+            output.add(insn);
+        }
+
+        /**
+         * Adds to the output suffix.
+         * 
+         * @param insn non-null; instruction to add
+         */
+        protected void addOutputSuffix(DalvInsn insn) {
+            output.addSuffix(insn);
+        }
+    }
+
+    /**
+     * Instruction visitor class for doing instruction translation with
+     * local variable tracking
+     */
+    private class LocalVariableAwareTranslationVisitor
+            extends TranslationVisitor {
+        /** non-null; local variable info */
+        private LocalVariableInfo locals;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param output non-null; destination for instruction output
+         * @param locals non-null; the local variable info
+         */
+        public LocalVariableAwareTranslationVisitor(OutputCollector output,
+                                                    LocalVariableInfo locals) {
+            super(output);
+            this.locals = locals;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitPlainInsn(PlainInsn insn) {
+            super.visitPlainInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            super.visitPlainCstInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitSwitchInsn(SwitchInsn insn) {
+            super.visitSwitchInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            super.visitThrowingCstInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            super.visitThrowingInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /**
+         * Adds a {@link LocalIntroduction} to the output if the given
+         * instruction in fact introduces a local variable.
+         * 
+         * @param insn non-null; instruction in question
+         */
+        public void addIntroductionIfNecessary(Insn insn) {
+            RegisterSpec spec = locals.getAssignment(insn);
+
+            if (spec != null) {
+                addOutput(new LocalIntroduction(insn.getPosition(), spec));
+            }
+        }
+    }    
+}
diff --git a/dx/src/com/android/dx/dex/code/SimpleInsn.java b/dx/src/com/android/dx/dex/code/SimpleInsn.java
new file mode 100644
index 0000000..ba20409
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SimpleInsn.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has no extra info beyond the basics provided for in
+ * the base class.
+ */
+public final class SimpleInsn extends FixedSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position non-null; source position
+     * @param registers non-null; register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     */
+    public SimpleInsn(Dop opcode, SourcePosition position,
+                      RegisterSpecList registers) {
+        super(opcode, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        return new SimpleInsn(opcode, getPosition(), getRegisters());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new SimpleInsn(getOpcode(), getPosition(), registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/SwitchData.java b/dx/src/com/android/dx/dex/code/SwitchData.java
new file mode 100644
index 0000000..1aaafa9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SwitchData.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Pseudo-instruction which holds switch data. The switch data is
+ * a map of values to target addresses, and this class writes the data
+ * in either a "packed" or "sparse" form.
+ */
+public final class SwitchData extends VariableSizeInsn {
+    /**
+     * non-null; address representing the instruction that uses this
+     * instance 
+     */
+    private final CodeAddress user;
+
+    /** non-null; sorted list of switch cases (keys) */
+    private final IntList cases;
+
+    /**
+     * non-null; corresponding list of code addresses; the branch
+     * target for each case 
+     */
+    private final CodeAddress[] targets;
+
+    /** whether the output table will be packed (vs. sparse) */
+    private final boolean packed;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param user non-null; address representing the instruction that
+     * uses this instance
+     * @param cases non-null; sorted list of switch cases (keys)
+     * @param targets non-null; corresponding list of code addresses; the
+     * branch target for each case
+     */
+    public SwitchData(SourcePosition position, CodeAddress user,
+                      IntList cases, CodeAddress[] targets) {
+        super(position, RegisterSpecList.EMPTY);
+
+        if (user == null) {
+            throw new NullPointerException("user == null");
+        }
+
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        if (targets == null) {
+            throw new NullPointerException("targets == null");
+        }
+
+        int sz = cases.size();
+
+        if (sz != targets.length) {
+            throw new IllegalArgumentException("cases / targets mismatch");
+        }
+
+        if (sz > 65535) {
+            throw new IllegalArgumentException("too many cases");
+        }
+
+        this.user = user;
+        this.cases = cases;
+        this.targets = targets;
+        this.packed = shouldPack(cases);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return packed ? (int) packedCodeSize(cases) :
+            (int) sparseCodeSize(cases);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        int baseAddress = user.getAddress();
+        int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
+        int sz = targets.length;
+
+        if (packed) {
+            int firstCase = (sz == 0) ? 0 : cases.get(0);
+            int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
+            int outSz = lastCase - firstCase + 1;
+
+            out.writeShort(0x100 | DalvOps.NOP);
+            out.writeShort(outSz);
+            out.writeInt(firstCase);
+
+            int caseAt = 0;
+            for (int i = 0; i < outSz; i++) {
+                int outCase = firstCase + i;
+                int oneCase = cases.get(caseAt);
+                int relTarget;
+
+                if (oneCase > outCase) {
+                    relTarget = defaultTarget;
+                } else {
+                    relTarget = targets[caseAt].getAddress() - baseAddress;
+                    caseAt++;
+                }
+
+                out.writeInt(relTarget);
+            }
+        } else {
+            out.writeShort(0x200 | DalvOps.NOP);
+            out.writeShort(sz);
+
+            for (int i = 0; i < sz; i++) {
+                out.writeInt(cases.get(i));
+            }
+
+            for (int i = 0; i < sz; i++) {
+                int relTarget = targets[i].getAddress() - baseAddress;
+                out.writeInt(relTarget);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new SwitchData(getPosition(), user, cases, targets);
+    }
+
+    /**
+     * Returns whether or not this instance's data will be output as packed.
+     * 
+     * @return <code>true</code> iff the data is to be packed
+     */
+    public boolean isPacked() {
+        return packed;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        int sz = targets.length;
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n    ");
+            sb.append(cases.get(i));
+            sb.append(": ");
+            sb.append(targets[i]);
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int baseAddress = user.getAddress();
+        StringBuffer sb = new StringBuffer(100);
+        int sz = targets.length;
+
+        sb.append(packed ? "packed" : "sparse");
+        sb.append("-switch-data // for switch @ ");
+        sb.append(Hex.u2(baseAddress));
+
+        for (int i = 0; i < sz; i++) {
+            int absTarget = targets[i].getAddress();
+            int relTarget = absTarget - baseAddress;
+            sb.append("\n  ");
+            sb.append(cases.get(i));
+            sb.append(": ");
+            sb.append(Hex.u4(absTarget));
+            sb.append(" // ");
+            sb.append(Hex.s4(relTarget));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the size of a packed table for the given cases, in 16-bit code
+     * units.
+     * 
+     * @param cases non-null; sorted list of cases
+     * @return &gt;= -1; the packed table size or <code>-1</code> if the
+     * cases couldn't possibly be represented as a packed table
+     */
+    private static long packedCodeSize(IntList cases) {
+        int sz = cases.size();
+        long low = cases.get(0);
+        long high = cases.get(sz - 1);
+        long result = ((high - low + 1)) * 2 + 4;
+
+        return (result <= 0x7fffffff) ? result : -1;
+    }
+
+    /**
+     * Gets the size of a sparse table for the given cases, in 16-bit code
+     * units.
+     * 
+     * @param cases non-null; sorted list of cases
+     * @return &gt; 0; the sparse table size
+     */
+    private static long sparseCodeSize(IntList cases) {
+        int sz = cases.size();
+
+        return (sz * 4L) + 2;
+    }
+
+    /**
+     * Determines whether the given list of cases warrant being packed.
+     * 
+     * @param cases non-null; sorted list of cases
+     * @return <code>true</code> iff the table encoding the cases
+     * should be packed
+     */
+    private static boolean shouldPack(IntList cases) {
+        int sz = cases.size();
+
+        if (sz < 2) {
+            return true;
+        }
+
+        long packedSize = packedCodeSize(cases);
+        long sparseSize = sparseCodeSize(cases);
+
+        /*
+         * We pick the packed representation if it is possible and
+         * would be as small or smaller than 5/4 of the sparse
+         * representation. That is, we accept some size overhead on
+         * the packed representation, since that format is faster to
+         * execute at runtime.
+         */
+        return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/TargetInsn.java b/dx/src/com/android/dx/dex/code/TargetInsn.java
new file mode 100644
index 0000000..5620795
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/TargetInsn.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has a single branch target.
+ */
+public final class TargetInsn extends FixedSizeInsn {
+    /** non-null; the branch target */
+    private CodeAddress target;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>), and the target is initially
+     * <code>null</code>.
+     * 
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position non-null; source position
+     * @param registers non-null; register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     * @param target non-null; the branch target
+     */
+    public TargetInsn(Dop opcode, SourcePosition position,
+                      RegisterSpecList registers, CodeAddress target) {
+        super(opcode, position, registers);
+
+        if (target == null) {
+            throw new NullPointerException("target == null");
+        }
+
+        this.target = target;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new TargetInsn(getOpcode(), getPosition(), registers, target);
+    }
+
+    /**
+     * Returns an instance that is just like this one, except that its
+     * opcode has the opposite sense (as a test; e.g. a
+     * <code>lt</code> test becomes a <code>ge</code>), and its branch
+     * target is replaced by the one given, and all set-once values
+     * associated with the class (such as its address) are reset.
+     * 
+     * @param target non-null; the new branch target
+     * @return non-null; an appropriately-constructed instance
+     */
+    public TargetInsn withNewTargetAndReversed(CodeAddress target) {
+        Dop opcode = getOpcode().getOppositeTest();
+
+        return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+    }
+
+    /**
+     * Gets the unique branch target of this instruction.
+     * 
+     * @return non-null; the branch target
+     */
+    public CodeAddress getTarget() {
+        return target;
+    }
+
+    /**
+     * Gets the target address of this instruction. This is only valid
+     * to call if the target instruction has been assigned an address,
+     * and it is merely a convenient shorthand for
+     * <code>getTarget().getAddress()</code>.
+     * 
+     * @return &gt;= 0; the target address
+     */
+    public int getTargetAddress() {
+        return target.getAddress();
+    }
+
+    /**
+     * Gets the branch offset of this instruction. This is only valid to
+     * call if both this and the target instruction each has been assigned
+     * an address, and it is merely a convenient shorthand for
+     * <code>getTargetAddress() - getAddress()</code>.
+     * 
+     * @return the branch offset
+     */
+    public int getTargetOffset() {
+        return target.getAddress() - getAddress();
+    }
+
+    /**
+     * Returns whether the target offset is known.
+     * 
+     * @return <code>true</code> if the target offset is known or
+     * <code>false</code> if not
+     */
+    public boolean hasTargetOffset() {
+        return hasAddress() && target.hasAddress();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        if (target == null) {
+            return "????";
+        }
+
+        return target.identifierString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/VariableSizeInsn.java b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
new file mode 100644
index 0000000..7884249
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction base class for variable-sized instructions.
+ */
+public abstract class VariableSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     * @param registers non-null; source registers
+     */
+    public VariableSizeInsn(SourcePosition position,
+                            RegisterSpecList registers) {
+        super(Dops.SPECIAL_FORMAT, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withOpcode(Dop opcode) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
new file mode 100644
index 0000000..2ddb181
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction base class for zero-size (no code emitted)
+ * instructions, which are generally used for tracking metainformation
+ * about the code they are adjacent to.
+ */
+public abstract class ZeroSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown (<code>-1</code>).
+     * 
+     * @param position non-null; source position
+     */
+    public ZeroSizeInsn(SourcePosition position) {
+        super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int codeSize() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(AnnotatedOutput out) {
+        // Nothing to do here, for this class.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withOpcode(Dop opcode) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10t.java b/dx/src/com/android/dx/dex/code/form/Form10t.java
new file mode 100644
index 0000000..8551012
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10t.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>10t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form10t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form10t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form10t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInByte(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form20t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, (offset & 0xff)));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10x.java b/dx/src/com/android/dx/dex/code/form/Form10x.java
new file mode 100644
index 0000000..7dc7c43
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10x.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>10x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form10x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form10x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form10x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        // This format has no arguments.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        return (insn instanceof SimpleInsn) &&
+            (insn.getRegisters().size() == 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        write(out, opcodeUnit(insn, 0));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11n.java b/dx/src/com/android/dx/dex/code/form/Form11n.java
new file mode 100644
index 0000000..b94038b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11n.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>11n</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form11n extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form11n();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form11n() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 4);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInNibble(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form21s.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf)));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11x.java b/dx/src/com/android/dx/dex/code/form/Form11x.java
new file mode 100644
index 0000000..d656147
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11x.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>11x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form11x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form11x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form11x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 1) &&
+            unsignedFitsInByte(regs.get(0).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out, opcodeUnit(insn, regs.get(0).getReg()));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form12x.java b/dx/src/com/android/dx/dex/code/form/Form12x.java
new file mode 100644
index 0000000..3ed8ce9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form12x.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.HighRegisterPrefix;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>12x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form12x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form12x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form12x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+
+        /*
+         * The (sz - 2) and (sz - 1) below makes this code work for
+         * both the two- and three-register ops. (See "case 3" in
+         * isCompatible(), below.)
+         */
+
+        return regs.get(sz - 2).regString() + ", " +
+            regs.get(sz - 1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof SimpleInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec rs1;
+        RegisterSpec rs2;
+
+        switch (regs.size()) {
+            case 2: {
+                rs1 = regs.get(0);
+                rs2 = regs.get(1);
+                break;
+            }
+            case 3: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 3-arg but where the first two args are identical.
+                 */
+                rs1 = regs.get(1);
+                rs2 = regs.get(2);
+                if (rs1.getReg() != regs.get(0).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        return unsignedFitsInNibble(rs1.getReg()) &&
+            unsignedFitsInNibble(rs2.getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form22x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+
+        /*
+         * The (sz - 2) and (sz - 1) below makes this code work for
+         * both the two- and three-register ops. (See "case 3" in
+         * isCompatible(), above.)
+         */
+
+        write(out, opcodeUnit(insn,
+                              makeByte(regs.get(sz - 2).getReg(),
+                                       regs.get(sz - 1).getReg())));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form20t.java b/dx/src/com/android/dx/dex/code/form/Form20t.java
new file mode 100644
index 0000000..341bef3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form20t.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>20t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form20t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form20t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form20t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form30t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, 0), (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21c.java b/dx/src/com/android/dx/dex/code/form/Form21c.java
new file mode 100644
index 0000000..5695e7a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21c.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>21c</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form21c extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec reg;
+
+        switch (regs.size()) {
+            case 1: {
+                reg = regs.get(0);
+                break;
+            }
+            case 2: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 2-arg but where the two args are identical.
+                 */
+                reg = regs.get(0);
+                if (reg.getReg() != regs.get(1).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        if (!unsignedFitsInByte(reg.getReg())) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        return (cst instanceof CstType) ||
+            (cst instanceof CstFieldRef) ||
+            (cst instanceof CstString);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31c.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) cpi);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21h.java b/dx/src/com/android/dx/dex/code/form/Form21h.java
new file mode 100644
index 0000000..cf4b471
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21h.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>21h</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form21h extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21h();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21h() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return
+            literalBitsComment(value,
+                    (regs.get(0).getCategory() == 1) ? 32 : 64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        // Where the high bits are depends on the category of the target.
+        if (regs.get(0).getCategory() == 1) {
+            int bits = cb.getIntBits();
+            return ((bits & 0xffff) == 0);
+        } else {
+            long bits = cb.getLongBits();
+            return ((bits & 0xffffffffffffL) == 0);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31i.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        short bits;
+
+        // Where the high bits are depends on the category of the target.
+        if (regs.get(0).getCategory() == 1) {
+            bits = (short) (cb.getIntBits() >>> 16);
+        } else {
+            bits = (short) (cb.getLongBits() >>> 48);
+        }
+
+        write(out, opcodeUnit(insn, regs.get(0).getReg()), bits);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21s.java b/dx/src/com/android/dx/dex/code/form/Form21s.java
new file mode 100644
index 0000000..df10f80
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21s.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>21s</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form21s extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21s();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21s() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 16);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form21h.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21t.java b/dx/src/com/android/dx/dex/code/form/Form21t.java
new file mode 100644
index 0000000..03f2ddf
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21t.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>21t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form21t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22b.java b/dx/src/com/android/dx/dex/code/form/Form22b.java
new file mode 100644
index 0000000..e2a777f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22b.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>22b</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form22b extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22b();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22b() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 8);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInByte(regs.get(0).getReg()) &&
+              unsignedFitsInByte(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInByte(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form22s.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              codeUnit(regs.get(1).getReg(), value & 0xff));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22c.java b/dx/src/com/android/dx/dex/code/form/Form22c.java
new file mode 100644
index 0000000..547eea8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22c.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>22c</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form22c extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+        
+        Constant cst = ci.getConstant();
+        return (cst instanceof CstType) ||
+            (cst instanceof CstFieldRef);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) cpi);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22s.java b/dx/src/com/android/dx/dex/code/form/Form22s.java
new file mode 100644
index 0000000..3ed173f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22s.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>22s</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form22s extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22s();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22s() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + regs.get(1).regString()
+            + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 16);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22t.java b/dx/src/com/android/dx/dex/code/form/Form22t.java
new file mode 100644
index 0000000..1034b92
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22t.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>22t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form22t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22x.java b/dx/src/com/android/dx/dex/code/form/Form22x.java
new file mode 100644
index 0000000..ee91e85
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22x.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>22x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form22x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 2) &&
+            unsignedFitsInByte(regs.get(0).getReg()) &&
+            unsignedFitsInShort(regs.get(1).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form23x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) regs.get(1).getReg());
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form23x.java b/dx/src/com/android/dx/dex/code/form/Form23x.java
new file mode 100644
index 0000000..c0a4482
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form23x.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>23x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form23x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form23x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form23x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + regs.get(2).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 3) &&
+            unsignedFitsInByte(regs.get(0).getReg()) &&
+            unsignedFitsInByte(regs.get(1).getReg()) &&
+            unsignedFitsInByte(regs.get(2).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form32x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              codeUnit(regs.get(1).getReg(), regs.get(2).getReg()));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form30t.java b/dx/src/com/android/dx/dex/code/form/Form30t.java
new file mode 100644
index 0000000..32e2efa
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form30t.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>30t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form30t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form30t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form30t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, 0),
+                (short) offset,
+                (short) (offset >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31c.java b/dx/src/com/android/dx/dex/code/form/Form31c.java
new file mode 100644
index 0000000..5837009
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31c.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>31c</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form31c extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec reg;
+
+        switch (regs.size()) {
+            case 1: {
+                reg = regs.get(0);
+                break;
+            }
+            case 2: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 2-arg but where the two args are identical.
+                 */
+                reg = regs.get(0);
+                if (reg.getReg() != regs.get(1).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        if (!unsignedFitsInByte(reg.getReg())) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return ((cst instanceof CstType) ||
+                (cst instanceof CstFieldRef) ||
+                (cst instanceof CstString));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+                opcodeUnit(insn, regs.get(0).getReg()),
+                (short) cpi,
+                (short) (cpi >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31i.java b/dx/src/com/android/dx/dex/code/form/Form31i.java
new file mode 100644
index 0000000..2855bcb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31i.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>31i</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form31i extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31i();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31i() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 32);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        return ((CstLiteralBits) cst).fitsInInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form51l.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value,
+              (short) (value >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31t.java b/dx/src/com/android/dx/dex/code/form/Form31t.java
new file mode 100644
index 0000000..5472687
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31t.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>31t</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form31t extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, regs.get(0).getReg()),
+                (short) offset,
+                (short) (offset >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form32x.java b/dx/src/com/android/dx/dex/code/form/Form32x.java
new file mode 100644
index 0000000..9c52d93
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form32x.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>32x</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form32x extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form32x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form32x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 2) &&
+            unsignedFitsInShort(regs.get(0).getReg()) &&
+            unsignedFitsInShort(regs.get(1).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        write(out,
+              opcodeUnit(insn, 0),
+              (short) regs.get(0).getReg(),
+              (short) regs.get(1).getReg());
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form35c.java b/dx/src/com/android/dx/dex/code/form/Form35c.java
new file mode 100644
index 0000000..6be55fc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form35c.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>35c</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form35c extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form35c();
+
+    /** Maximal number of operands */
+    private static final int MAX_NUM_OPS = 5;
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form35c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = explicitize(insn.getRegisters());
+        return regListString(regs) + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        if (!((cst instanceof CstMethodRef) ||
+              (cst instanceof CstType))) {
+            return false;
+        }
+
+        RegisterSpecList regs = ci.getRegisters();
+        return (wordCount(regs) >= 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form3rc.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int cpi = ((CstInsn) insn).getIndex();
+        RegisterSpecList regs = explicitize(insn.getRegisters());
+        int sz = regs.size();
+        int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
+        int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
+        int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
+        int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
+        int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
+
+        write(out,
+              opcodeUnit(insn, 
+                         makeByte(r4, sz)), // encode the fifth operand here
+              (short) cpi,
+              codeUnit(r0, r1, r2, r3));
+    }
+
+    /**
+     * Gets the number of words required for the given register list, where
+     * category-2 values count as two words. Return <code>-1</code> if the
+     * list requires more than five words or contains registers that need
+     * more than a nibble to identify them.
+     * 
+     * @param regs non-null; the register list in question
+     * @return &gt;= -1; the number of words required, or <code>-1</code> 
+     * if the list couldn't possibly fit in this format
+     */
+    private static int wordCount(RegisterSpecList regs) {
+        int sz = regs.size();
+
+        if (sz > MAX_NUM_OPS) {
+            // It can't possibly fit.
+            return -1;
+        }
+
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = regs.get(i);
+            result += one.getCategory();
+            /*
+             * The check below adds (category - 1) to the register, to
+             * account for the fact that the second half of a
+             * category-2 register has to be represented explicitly in
+             * the result.
+             */
+            if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
+                return -1;
+            }
+        }
+
+        return (result <= MAX_NUM_OPS) ? result : -1;
+    }
+
+    /**
+     * Returns a register list which is equivalent to the given one,
+     * except that it splits category-2 registers into two explicit
+     * entries. This returns the original list if no modification is
+     * required
+     * 
+     * @param orig non-null; the original list
+     * @return non-null; the list with the described transformation
+     */
+    private static RegisterSpecList explicitize(RegisterSpecList orig) {
+        int wordCount = wordCount(orig);
+        int sz = orig.size();
+
+        if (wordCount == sz) {
+            return orig;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(wordCount);
+        int wordAt = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = orig.get(i);
+            result.set(wordAt, one);
+            if (one.getCategory() == 2) {
+                result.set(wordAt + 1,
+                           RegisterSpec.make(one.getReg() + 1, Type.VOID));
+                wordAt += 2;
+            } else {
+                wordAt++;
+            }
+        }
+
+        result.setImmutable();
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form3rc.java b/dx/src/com/android/dx/dex/code/form/Form3rc.java
new file mode 100644
index 0000000..0accbc2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form3rc.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>3rc</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form3rc extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form3rc();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form3rc() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int size = regs.size();
+        StringBuilder sb = new StringBuilder(30);
+
+        sb.append("{");
+
+        switch (size) {
+            case 0: {
+                // Nothing to do.
+                break;
+            }
+            case 1: {
+                sb.append(regs.get(0).regString());
+                break;
+            }
+            default: {
+                RegisterSpec lastReg = regs.get(size - 1);
+                if (lastReg.getCategory() == 2) {
+                    /*
+                     * Add one to properly represent a list-final
+                     * category-2 register.
+                     */
+                    lastReg = lastReg.withOffset(1);
+                }
+                
+                sb.append(regs.get(0).regString());
+                sb.append("..");
+                sb.append(lastReg.regString());
+            }
+        }
+
+        sb.append("}, ");
+        sb.append(cstString(insn));
+        
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        if (!((cst instanceof CstMethodRef) ||
+              (cst instanceof CstType))) {
+            return false;
+        }
+
+        RegisterSpecList regs = ci.getRegisters();
+        int sz = regs.size();
+
+        if (sz == 0) {
+            return true;
+        }
+
+        int first = regs.get(0).getReg();
+        int next = first;
+
+        if (!unsignedFitsInShort(first)) {
+            return false;
+        }
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = regs.get(i);
+            if (one.getReg() != next) {
+                return false;
+            }
+            next += one.getCategory();
+        }
+
+        return unsignedFitsInByte(next - first);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+        int cpi = ((CstInsn) insn).getIndex();
+        int firstReg;
+        int count;
+
+        if (sz == 0) {
+            firstReg = 0;
+            count = 0;
+        } else {
+            int lastReg = regs.get(sz - 1).getNextReg();
+            firstReg = regs.get(0).getReg();
+            count = lastReg - firstReg;
+        }
+
+        write(out,
+              opcodeUnit(insn, count),
+              (short) cpi,
+              (short) firstReg);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form51l.java b/dx/src/com/android/dx/dex/code/form/Form51l.java
new file mode 100644
index 0000000..09a32f6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form51l.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format <code>51l</code>. See the instruction format spec
+ * for details.
+ */
+public final class Form51l extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form51l();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form51l() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 5;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return (cst instanceof CstLiteral64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        long value =
+            ((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value,
+              (short) (value >> 16),
+              (short) (value >> 32),
+              (short) (value >> 48));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/SpecialFormat.java b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
new file mode 100644
index 0000000..79efd67
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvOps;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format for nonstandard format instructions, which aren't
+ * generally real instructions but do end up appearing in instruction
+ * lists. Most of the overridden methods on this class end up throwing
+ * exceptions, as code should know (implicitly or explicitly) to avoid
+ * using this class. The one exception is {@link #isCompatible}, which
+ * always returns <code>true</code>.
+ */
+public final class SpecialFormat extends InsnFormat {
+    /** non-null; unique instance of this class */
+    public static final InsnFormat THE_ONE = new SpecialFormat();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private SpecialFormat() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        throw new RuntimeException("unsupported");
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationItem.java b/dx/src/com/android/dx/dex/file/AnnotationItem.java
new file mode 100644
index 0000000..43ac362
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationItem.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Single annotation, which consists of a type and a set of name-value
+ * element pairs.
+ */
+public final class AnnotationItem extends OffsettedItem {
+    /** annotation visibility constant: visible at build time only */
+    private static final int VISIBILITY_BUILD = 0;
+    
+    /** annotation visibility constant: visible at runtime */
+    private static final int VISIBILITY_RUNTIME = 1;
+
+    /** annotation visibility constant: visible at runtime only to system */
+    private static final int VISIBILITY_SYSTEM = 2;
+    
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    /** non-null; unique instance of {@link #TypeIdSorter} */
+    private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
+    
+    /** non-null; the annotation to represent */
+    private final Annotation annotation;
+
+    /**
+     * null-ok; type reference for the annotation type; set during
+     * {@link #addContents}
+     */
+    private TypeIdItem type;
+
+    /**
+     * null-ok; encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Comparator that sorts (outer) instances by type id index.
+     */
+    private static class TypeIdSorter implements Comparator<AnnotationItem> {
+        /** {@inheritDoc} */
+        public int compare(AnnotationItem item1, AnnotationItem item2) {
+            int index1 = item1.type.getIndex();
+            int index2 = item2.type.getIndex();
+
+            if (index1 < index2) {
+                return -1;
+            } else if (index1 > index2) {
+                return 1;
+            }
+
+            return 0;
+        }
+    }
+
+    /**
+     * Sorts an array of instances, in place, by type id index,
+     * ignoring all other aspects of the elements. This is only valid
+     * to use after type id indices are known.
+     * 
+     * @param array non-null; array to sort
+     */
+    public static void sortByTypeIdIndex(AnnotationItem[] array) {
+        Arrays.sort(array, TYPE_ID_SORTER);
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotation non-null; annotation to represent
+     */
+    public AnnotationItem(Annotation annotation) {
+        /*
+         * The write size isn't known up-front because (the variable-lengthed)
+         * leb128 type is used to represent some things.
+         */
+        super(ALIGNMENT, -1);
+
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        this.annotation = annotation;
+        this.type = null;
+        this.encodedForm = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotation.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        AnnotationItem otherAnnotation = (AnnotationItem) other;
+
+        return annotation.compareTo(otherAnnotation.annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotation.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        type = file.getTypeIds().intern(annotation.getType());
+        ValueEncoder.addContents(file, annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+        encoder.writeAnnotation(annotation, false);
+        encodedForm = out.toByteArray();
+
+        // Add one for the visibility byte in front of the encoded annotation.
+        setWriteSize(encodedForm.length + 1);
+    }
+
+    /**
+     * Write a (listing file) annotation for this instance to the given
+     * output, that consumes no bytes of output. This is for annotating
+     * a reference to this instance at the point of the reference.
+     * 
+     * @param out non-null; where to output to
+     * @param prefix non-null; prefix for each line of output
+     */
+    public void annotateTo(AnnotatedOutput out, String prefix) {
+        out.annotate(0, prefix + "visibility: " +
+                annotation.getVisibility().toHuman());
+        out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
+
+        for (NameValuePair pair : annotation.getNameValuePairs()) {
+            CstUtf8 name = pair.getName();
+            Constant value = pair.getValue();
+
+            out.annotate(0, prefix + name.toHuman() + ": " +
+                    ValueEncoder.constantToHuman(value));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        AnnotationVisibility visibility = annotation.getVisibility();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotation");
+            out.annotate(1, "  visibility: VISBILITY_" + visibility);
+        }
+
+        switch (visibility) {
+            case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
+            case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
+            case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
+            default: {
+                // EMBEDDED shouldn't appear at the top level.
+                throw new RuntimeException("shouldn't happen");
+            }
+        }
+        
+        if (annotates) {
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            ValueEncoder encoder = new ValueEncoder(file, out);
+            encoder.writeAnnotation(annotation, true);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
new file mode 100644
index 0000000..f03cc08
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Set of annotations, where no annotation type appears more than once.
+ */
+public final class AnnotationSetItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** the size of an entry int the set: one <code>uint</code> */
+    private static final int ENTRY_WRITE_SIZE = 4;
+
+    /** non-null; the set of annotations */
+    private final Annotations annotations;
+    
+    /**
+     * non-null; set of annotations as individual items in an array.
+     * <b>Note:</b> The contents have to get sorted by type id before
+     * writing.
+     */
+    private final AnnotationItem[] items;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotations non-null; set of annotations
+     */
+    public AnnotationSetItem(Annotations annotations) {
+        super(ALIGNMENT, writeSize(annotations));
+
+        this.annotations = annotations;
+        this.items = new AnnotationItem[annotations.size()];
+
+        int at = 0;
+        for (Annotation a : annotations.getAnnotations()) {
+            items[at] = new AnnotationItem(a);
+            at++;
+        }
+    }
+
+    /**
+     * Gets the write size for the given set.
+     * 
+     * @param annotations non-null; the set
+     * @return &gt; 0; the write size
+     */
+    private static int writeSize(Annotations annotations) {
+        // This includes an int size at the start of the list.
+
+        try {
+            return (annotations.size() * ENTRY_WRITE_SIZE) + 4;
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("list == null");
+        }
+    }
+
+    /**
+     * Gets the underlying annotations of this instance
+     * 
+     * @return non-null; the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotations.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        AnnotationSetItem otherSet = (AnnotationSetItem) other;
+
+        return annotations.compareTo(otherSet.annotations);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_SET_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotations.toString();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection byteData = file.getByteData();
+        int size = items.length;
+
+        for (int i = 0; i < size; i++) {
+            items[i] = byteData.intern(items[i]);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Sort the array to be in type id index order.
+        AnnotationItem.sortByTypeIdIndex(items);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int size = items.length;
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotation set");
+            out.annotate(4, "  size: " + Hex.u4(size));
+        }
+
+        out.writeInt(size);
+        
+        for (int i = 0; i < size; i++) {
+            AnnotationItem item = items[i];
+            int offset = item.getAbsoluteOffset();
+            
+            if (annotates) {
+                out.annotate(4, "  entries[" + Integer.toHexString(i) + "]: " +
+                        Hex.u4(offset));
+                items[i].annotateTo(out, "    ");
+            }
+
+            out.writeInt(offset);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
new file mode 100644
index 0000000..422e2f0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Indirect reference to an {@link AnnotationSetItem}.
+ */
+public final class AnnotationSetRefItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class, in bytes */
+    private static final int WRITE_SIZE = 4;
+
+    /** non-null; the annotation set to refer to */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param annotations non-null; the annotation set to refer to
+     */
+    public AnnotationSetRefItem(AnnotationSetItem annotations) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_SET_REF_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection wordData = file.getWordData();
+
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotations.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "  annotations_off: " + Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(annotationsOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
new file mode 100644
index 0000000..c9d7968
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+import java.util.ArrayList;
+
+import static com.android.dx.rop.annotation.AnnotationVisibility.*;
+
+/**
+ * Utility class for dealing with annotations.
+ */
+public final class AnnotationUtils {
+    /** non-null; type for <code>AnnotationDefault</code> annotations */
+    private static final CstType ANNOTATION_DEFAULT_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
+
+    /** non-null; type for <code>EnclosingClass</code> annotations */
+    private static final CstType ENCLOSING_CLASS_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));
+
+    /** non-null; type for <code>EnclosingMethod</code> annotations */
+    private static final CstType ENCLOSING_METHOD_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));
+
+    /** non-null; type for <code>InnerClass</code> annotations */
+    private static final CstType INNER_CLASS_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));
+
+    /** non-null; type for <code>MemberClasses</code> annotations */
+    private static final CstType MEMBER_CLASSES_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));
+
+    /** non-null; type for <code>Signature</code> annotations */
+    private static final CstType SIGNATURE_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));
+
+    /** non-null; type for <code>Throws</code> annotations */
+    private static final CstType THROWS_TYPE = 
+        CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));
+
+    /** non-null; the UTF-8 constant <code>"accessFlags"</code> */
+    private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags");
+
+    /** non-null; the UTF-8 constant <code>"name"</code> */
+    private static final CstUtf8 NAME_UTF = new CstUtf8("name");
+
+    /** non-null; the UTF-8 constant <code>"value"</code> */
+    private static final CstUtf8 VALUE_UTF = new CstUtf8("value");
+
+    /**
+     * This class is uninstantiable.
+     */
+    private AnnotationUtils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Constructs a standard <code>AnnotationDefault</code> annotation.
+     * 
+     * @param defaults non-null; the defaults, itself as an annotation
+     * @return non-null; the constructed annotation
+     */
+    public static Annotation makeAnnotationDefault(Annotation defaults) {
+        Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, new CstAnnotation(defaults)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>EnclosingClass</code> annotation.
+     * 
+     * @param clazz non-null; the enclosing class
+     * @return non-null; the annotation
+     */
+    public static Annotation makeEnclosingClass(CstType clazz) {
+        Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, clazz));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>EnclosingMethod</code> annotation.
+     * 
+     * @param method non-null; the enclosing method
+     * @return non-null; the annotation
+     */
+    public static Annotation makeEnclosingMethod(CstMethodRef method) {
+        Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, method));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>InnerClass</code> annotation.
+     * 
+     * @param name null-ok; the original name of the class, or
+     * <code>null</code> to represent an anonymous class
+     * @param accessFlags the original access flags
+     * @return non-null; the annotation
+     */
+    public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) {
+        Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
+        Constant nameCst =
+            (name != null) ? new CstString(name) : CstKnownNull.THE_ONE;
+
+        result.put(new NameValuePair(NAME_UTF, nameCst));
+        result.put(new NameValuePair(ACCESS_FLAGS_UTF,
+                        CstInteger.make(accessFlags)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>MemberClasses</code> annotation.
+     * 
+     * @param types non-null; the list of (the types of) the member classes
+     * @return non-null; the annotation
+     */
+    public static Annotation makeMemberClasses(TypeList types) {
+        CstArray array = makeCstArray(types);
+        Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
+        result.put(new NameValuePair(VALUE_UTF, array));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>Signature</code> annotation.
+     * 
+     * @param signature non-null; the signature string
+     * @return non-null; the annotation
+     */
+    public static Annotation makeSignature(CstUtf8 signature) {
+        Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);
+
+        /*
+         * Split the string into pieces that are likely to be common
+         * across many signatures and the rest of the file.
+         */
+         
+        String raw = signature.getString();
+        int rawLength = raw.length();
+        ArrayList<String> pieces = new ArrayList<String>(20);
+
+        for (int at = 0; at < rawLength; /*at*/) {
+            char c = raw.charAt(at);
+            int endAt = at + 1;
+            if (c == 'L') {
+                // Scan to ';' or '<'. Consume ';' but not '<'.
+                while (endAt < rawLength) {
+                    c = raw.charAt(endAt);
+                    if (c == ';') {
+                        endAt++;
+                        break;
+                    } else if (c == '<') {
+                        break;
+                    }
+                    endAt++;
+                }
+            } else {
+                // Scan to 'L' without consuming it.
+                while (endAt < rawLength) {
+                    c = raw.charAt(endAt);
+                    if (c == 'L') {
+                        break;
+                    }
+                    endAt++;
+                }
+            }
+
+            pieces.add(raw.substring(at, endAt));
+            at = endAt;
+        }
+
+        int size = pieces.size();
+        CstArray.List list = new CstArray.List(size);
+
+        for (int i = 0; i < size; i++) {
+            list.set(i, new CstString(pieces.get(i)));
+        }
+
+        list.setImmutable();
+        
+        result.put(new NameValuePair(VALUE_UTF, new CstArray(list)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard <code>Throws</code> annotation.
+     * 
+     * @param types non-null; the list of thrown types
+     * @return non-null; the annotation
+     */
+    public static Annotation makeThrows(TypeList types) {
+        CstArray array = makeCstArray(types);
+        Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
+        result.put(new NameValuePair(VALUE_UTF, array));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Converts a {@link TypeList} to a {@link CstArray}.
+     * 
+     * @param types non-null; the type list
+     * @return non-null; the corresponding array constant
+     */
+    private static CstArray makeCstArray(TypeList types) {
+        int size = types.size();
+        CstArray.List list = new CstArray.List(size);
+
+        for (int i = 0; i < size; i++) {
+            list.set(i, CstType.intern(types.getType(i)));
+        }
+
+        list.setImmutable();
+        return new CstArray(list);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..4521e4c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Per-class directory of annotations.
+ */
+public final class AnnotationsDirectoryItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class's header, in bytes */
+    private static final int HEADER_SIZE = 16;
+
+    /** write size of a list element, in bytes */
+    private static final int ELEMENT_SIZE = 8;
+
+    /** null-ok; the class-level annotations, if any */
+    private AnnotationSetItem classAnnotations;
+    
+    /** null-ok; the annotated fields, if any */
+    private ArrayList<FieldAnnotationStruct> fieldAnnotations;
+
+    /** null-ok; the annotated methods, if any */
+    private ArrayList<MethodAnnotationStruct> methodAnnotations;
+
+    /** null-ok; the annotated parameters, if any */
+    private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
+
+    /**
+     * Constructs an empty instance.
+     */
+    public AnnotationsDirectoryItem() {
+        super(ALIGNMENT, -1);
+
+        classAnnotations = null;
+        fieldAnnotations = null;
+        methodAnnotations = null;
+        parameterAnnotations = null;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
+    }
+
+    /**
+     * Returns whether this item is empty (has no contents).
+     * 
+     * @return <code>true</code> if this item is empty, or <code>false</code>
+     * if not
+     */
+    public boolean isEmpty() {
+        return (classAnnotations == null) &&
+            (fieldAnnotations == null) &&
+            (methodAnnotations == null) &&
+            (parameterAnnotations == null);
+    }
+
+    /**
+     * Returns whether this item is a candidate for interning. The only
+     * interning candidates are ones that <i>only</i> have a non-null
+     * set of class annotations, with no other lists.
+     *
+     * @return <code>true</code> if this is an interning candidate, or
+     * <code>false</code> if not
+     */
+    public boolean isInternable() {
+        return (classAnnotations != null) &&
+            (fieldAnnotations == null) &&
+            (methodAnnotations == null) &&
+            (parameterAnnotations == null);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        if (classAnnotations == null) {
+            return 0;
+        }
+        
+        return classAnnotations.hashCode();
+    }
+    
+    /**
+     * {@inheritDoc}
+     * 
+     * <p><b>Note:</b>: This throws an exception if this item is not
+     * internable.</p>
+     * 
+     * @see #isInternable
+     */
+    @Override
+    public int compareTo0(OffsettedItem other) {
+        if (! isInternable()) {
+            throw new UnsupportedOperationException("uninternable instance");
+        }
+
+        AnnotationsDirectoryItem otherDirectory =
+            (AnnotationsDirectoryItem) other;
+        return classAnnotations.compareTo(otherDirectory.classAnnotations);
+    }
+
+    /**
+     * Sets the direct annotations on this instance. These are annotations
+     * made on the class, per se, as opposed to on one of its members.
+     * It is only valid to call this method at most once per instance.
+     * 
+     * @param annotations non-null; annotations to set for this class
+     */
+    public void setClassAnnotations(Annotations annotations) {
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+        
+        if (classAnnotations != null) {
+            throw new UnsupportedOperationException(
+                    "class annotations already set");
+        }
+
+        classAnnotations = new AnnotationSetItem(annotations);
+    }
+
+    /**
+     * Adds a field annotations item to this instance.
+     * 
+     * @param field non-null; field in question
+     * @param annotations non-null; associated annotations to add
+     */
+    public void addFieldAnnotations(CstFieldRef field,
+            Annotations annotations) {
+        if (fieldAnnotations == null) {
+            fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
+        }
+        
+        fieldAnnotations.add(new FieldAnnotationStruct(field,
+                        new AnnotationSetItem(annotations)));
+    }
+
+    /**
+     * Adds a method annotations item to this instance.
+     * 
+     * @param method non-null; method in question
+     * @param annotations non-null; associated annotations to add
+     */
+    public void addMethodAnnotations(CstMethodRef method,
+            Annotations annotations) {
+        if (methodAnnotations == null) {
+            methodAnnotations = new ArrayList<MethodAnnotationStruct>();
+        }
+
+        methodAnnotations.add(new MethodAnnotationStruct(method,
+                        new AnnotationSetItem(annotations)));
+    }
+
+    /**
+     * Adds a parameter annotations item to this instance.
+     * 
+     * @param method non-null; method in question
+     * @param list non-null; associated list of annotation sets to add
+     */
+    public void addParameterAnnotations(CstMethodRef method,
+            AnnotationsList list) {
+        if (parameterAnnotations == null) {
+            parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
+        }
+
+        parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
+    }
+
+    /**
+     * Gets the method annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     * 
+     * @param method non-null; the method
+     * @return null-ok; the method annotations, if any
+     */
+    public Annotations getMethodAnnotations(CstMethodRef method) {
+        if (methodAnnotations == null) {
+            return null;
+        }
+        
+        for (MethodAnnotationStruct item : methodAnnotations) {
+            if (item.getMethod().equals(method)) {
+                return item.getAnnotations();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the parameter annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     * 
+     * @param method non-null; the method
+     * @return null-ok; the parameter annotations, if any
+     */
+    public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+        if (parameterAnnotations == null) {
+            return null;
+        }
+
+        for (ParameterAnnotationStruct item : parameterAnnotations) {
+            if (item.getMethod().equals(method)) {
+                return item.getAnnotationsList();
+            }
+        }
+
+        return null;
+    }
+    
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection wordData = file.getWordData();
+
+        if (classAnnotations != null) {
+            classAnnotations = wordData.intern(classAnnotations);
+        }
+
+        if (fieldAnnotations != null) {
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                item.addContents(file);
+            }
+        }
+
+        if (methodAnnotations != null) {
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                item.addContents(file);
+            }
+        }
+        
+        if (parameterAnnotations != null) {
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                item.addContents(file);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // We just need to set the write size here.
+
+        int elementCount = listSize(fieldAnnotations)
+            + listSize(methodAnnotations) + listSize(parameterAnnotations);
+        setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
+        int fieldsSize = listSize(fieldAnnotations);
+        int methodsSize = listSize(methodAnnotations);
+        int parametersSize = listSize(parameterAnnotations);
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotations directory");
+            out.annotate(4, "  class_annotations_off: " + Hex.u4(classOff));
+            out.annotate(4, "  fields_size:           " +
+                    Hex.u4(fieldsSize));
+            out.annotate(4, "  methods_size:          " +
+                    Hex.u4(methodsSize));
+            out.annotate(4, "  parameters_size:       " +
+                    Hex.u4(parametersSize));
+        }
+
+        out.writeInt(classOff);
+        out.writeInt(fieldsSize);
+        out.writeInt(methodsSize);
+        out.writeInt(parametersSize);
+
+        if (fieldsSize != 0) {
+            Collections.sort(fieldAnnotations);
+            if (annotates) {
+                out.annotate(0, "  fields:");
+            }
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+
+        if (methodsSize != 0) {
+            Collections.sort(methodAnnotations);
+            if (annotates) {
+                out.annotate(0, "  methods:");
+            }
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+
+        if (parametersSize != 0) {
+            Collections.sort(parameterAnnotations);
+            if (annotates) {
+                out.annotate(0, "  parameters:");
+            }
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+    }
+
+    /**
+     * Gets the list size of the given list, or <code>0</code> if given
+     * <code>null</code>.
+     * 
+     * @param list null-ok; the list in question
+     * @return &gt;= 0; its size
+     */
+    private static int listSize(ArrayList<?> list) {
+        if (list == null) {
+            return 0;
+        }
+
+        return list.size();
+    }
+
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way. This is meant to be called from {@link ClassDefItem#debugPrint}.
+     * 
+     * @param out non-null; where to output to
+     */
+    /*package*/ void debugPrint(PrintWriter out) {
+        if (classAnnotations != null) {
+            out.println("  class annotations: " + classAnnotations);
+        }
+
+        if (fieldAnnotations != null) {
+            out.println("  field annotations:");
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+
+        if (methodAnnotations != null) {
+            out.println("  method annotations:");
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+        
+        if (parameterAnnotations != null) {
+            out.println("  parameter annotations:");
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+    }    
+}
diff --git a/dx/src/com/android/dx/dex/file/CatchStructs.java b/dx/src/com/android/dx/dex/file/CatchStructs.java
new file mode 100644
index 0000000..b7abc3f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CatchStructs.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CatchHandlerList;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * List of exception handlers (tuples of covered range, catch type,
+ * handler address) for a particular piece of code. Instances of this
+ * class correspond to a <code>try_item[]</code> and a
+ * <code>catch_handler_item[]</code>.
+ */
+public final class CatchStructs {
+    /**
+     * the size of a <code>try_item</code>: a <code>uint</code>
+     * and two <code>ushort</code>s 
+     */
+    private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2);
+
+    /** non-null; code that contains the catches */
+    private final DalvCode code;
+    
+    /**
+     * null-ok; the underlying table; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private CatchTable table;
+
+    /**
+     * null-ok; the encoded handler list, if calculated; set in
+     * {@link #encode}
+     */
+    private byte[] encodedHandlers;
+
+    /**
+     * length of the handlers header (encoded size), if known; used for
+     * annotation
+     */
+    private int encodedHandlerHeaderSize;
+
+    /**
+     * null-ok; map from handler lists to byte offsets, if calculated; set in
+     * {@link #encode}
+     */
+    private TreeMap<CatchHandlerList, Integer> handlerOffsets;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param code non-null; code that contains the catches
+     */
+    public CatchStructs(DalvCode code) {
+        this.code = code;
+        this.table = null;
+        this.encodedHandlers = null;
+        this.encodedHandlerHeaderSize = 0;
+        this.handlerOffsets = null;
+    }
+
+    /**
+     * Finish processing the catches, if necessary.
+     */
+    private void finishProcessingIfNecessary() {
+        if (table == null) {
+            table = code.getCatches();
+        }
+    }
+
+    /**
+     * Gets the size of the tries list, in entries.
+     * 
+     * @return &gt;= 0; the tries list size
+     */
+    public int triesSize() {
+        finishProcessingIfNecessary();
+        return table.size();
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     * 
+     * @param out non-null; where to dump
+     * @param prefix non-null; prefix to attach to each line of output
+     */
+    public void debugPrint(PrintWriter out, String prefix) {
+        annotateEntries(prefix, out, null);
+    }
+
+    /**
+     * Encodes the handler lists.
+     * 
+     * @param file non-null; file this instance is part of
+     */
+    public void encode(DexFile file) {
+        finishProcessingIfNecessary();
+
+        TypeIdsSection typeIds = file.getTypeIds();
+        int size = table.size();
+
+        handlerOffsets = new TreeMap<CatchHandlerList, Integer>();
+
+        /*
+         * First add a map entry for each unique list. The tree structure
+         * will ensure they are sorted when we reiterate later.
+         */
+        for (int i = 0; i < size; i++) {
+            handlerOffsets.put(table.get(i).getHandlers(), null);
+        }
+
+        if (handlerOffsets.size() > 65535) {
+            throw new UnsupportedOperationException(
+                    "too many catch handlers");
+        }
+        
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+        // Write out the handlers "header" consisting of its size in entries.
+        encodedHandlerHeaderSize =
+            out.writeUnsignedLeb128(handlerOffsets.size());
+        
+        // Now write the lists out in order, noting the offset of each.
+        for (Map.Entry<CatchHandlerList, Integer> mapping :
+                 handlerOffsets.entrySet()) {
+            CatchHandlerList list = mapping.getKey();
+            int listSize = list.size();
+            boolean catchesAll = list.catchesAll();
+
+            // Set the offset before we do any writing.
+            mapping.setValue(out.getCursor());
+
+            if (catchesAll) {
+                // A size <= 0 means that the list ends with a catch-all.
+                out.writeSignedLeb128(-(listSize - 1));
+                listSize--;
+            } else {
+                out.writeSignedLeb128(listSize);
+            }
+
+            for (int i = 0; i < listSize; i++) {
+                CatchHandlerList.Entry entry = list.get(i);
+                out.writeUnsignedLeb128(
+                        typeIds.indexOf(entry.getExceptionType()));
+                out.writeUnsignedLeb128(entry.getHandler());
+            }
+
+            if (catchesAll) {
+                out.writeUnsignedLeb128(list.get(listSize).getHandler());
+            }
+        }
+
+        encodedHandlers = out.toByteArray();
+    }
+
+    /**
+     * Gets the write size of this instance, in bytes.
+     * 
+     * @return &gt;= 0; the write size
+     */
+    public int writeSize() {
+        return (triesSize() * TRY_ITEM_WRITE_SIZE) +
+                + encodedHandlers.length;
+    }
+    
+    /**
+     * Writes this instance to the given stream.
+     * 
+     * @param file non-null; file this instance is part of
+     * @param out non-null; where to write to
+     */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        finishProcessingIfNecessary();
+
+        TypeIdsSection typeIds = file.getTypeIds();
+        int tableSize = table.size();
+        int handlersSize = handlerOffsets.size();
+        
+        if (out.annotates()) {
+            annotateEntries("  ", null, out);
+        }
+
+        for (int i = 0; i < tableSize; i++) {
+            CatchTable.Entry one = table.get(i);
+            int start = one.getStart();
+            int end = one.getEnd();
+            int insnCount = end - start;
+
+            if (insnCount >= 65536) {
+                throw new UnsupportedOperationException(
+                        "bogus exception range: " + Hex.u4(start) + ".." +
+                        Hex.u4(end));
+            }
+
+            out.writeInt(start);
+            out.writeShort(insnCount);
+            out.writeShort(handlerOffsets.get(one.getHandlers()));
+        }
+        
+        out.write(encodedHandlers);
+    }
+
+    /**
+     * Helper method to annotate or simply print the exception handlers.
+     * Only one of <code>printTo</code> or <code>annotateTo</code> should
+     * be non-null.
+     * 
+     * @param prefix non-null; prefix for each line
+     * @param printTo null-ok; where to print to
+     * @param annotateTo null-ok; where to consume bytes and annotate to
+     */
+    private void annotateEntries(String prefix, PrintWriter printTo,
+            AnnotatedOutput annotateTo) {
+        finishProcessingIfNecessary();
+
+        boolean consume = (annotateTo != null);
+        int amt1 = consume ? 6 : 0;
+        int amt2 = consume ? 2 : 0;
+        int size = table.size();
+        String subPrefix = prefix + "  ";
+
+        if (consume) {
+            annotateTo.annotate(0, prefix + "tries:");
+        } else {
+            printTo.println(prefix + "tries:");
+        }
+
+        for (int i = 0; i < size; i++) {
+            CatchTable.Entry entry = table.get(i);
+            CatchHandlerList handlers = entry.getHandlers();
+            String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart())
+                + ".." + Hex.u2or4(entry.getEnd());
+            String s2 = handlers.toHuman(subPrefix, "");
+
+            if (consume) {
+                annotateTo.annotate(amt1, s1);
+                annotateTo.annotate(amt2, s2);
+            } else {
+                printTo.println(s1);
+                printTo.println(s2);
+            }
+        }
+
+        if (! consume) {
+            // Only emit the handler lists if we are consuming bytes.
+            return;
+        }
+
+        annotateTo.annotate(0, prefix + "handlers:");
+        annotateTo.annotate(encodedHandlerHeaderSize,
+                subPrefix + "size: " + Hex.u2(handlerOffsets.size()));
+
+        int lastOffset = 0;
+        CatchHandlerList lastList = null;
+        
+        for (Map.Entry<CatchHandlerList, Integer> mapping :
+                 handlerOffsets.entrySet()) {
+            CatchHandlerList list = mapping.getKey();
+            int offset = mapping.getValue();
+
+            if (lastList != null) {
+                annotateAndConsumeHandlers(lastList, lastOffset,
+                        offset - lastOffset, subPrefix, printTo, annotateTo);
+            }
+
+            lastList = list;
+            lastOffset = offset;
+        }
+
+        annotateAndConsumeHandlers(lastList, lastOffset,
+                encodedHandlers.length - lastOffset,
+                subPrefix, printTo, annotateTo);
+    }
+
+    /**
+     * Helper for {@link #annotateEntries} to annotate a catch handler list
+     * while consuming it.
+     * 
+     * @param handlers non-null; handlers to annotate
+     * @param offset &gt;= 0; the offset of this handler
+     * @param size &gt;= 1; the number of bytes the handlers consume
+     * @param prefix non-null; prefix for each line
+     * @param printTo null-ok; where to print to
+     * @param annotateTo non-null; where to annotate to
+     */
+    private static void annotateAndConsumeHandlers(CatchHandlerList handlers,
+            int offset, int size, String prefix, PrintWriter printTo,
+            AnnotatedOutput annotateTo) {
+        String s = handlers.toHuman(prefix, Hex.u2(offset) + ": ");
+
+        if (printTo != null) {
+            printTo.println(s);
+        }
+
+        annotateTo.annotate(size, s);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDataItem.java b/dx/src/com/android/dx/dex/file/ClassDataItem.java
new file mode 100644
index 0000000..638daed
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.Zeroes;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * Representation of all the parts of a Dalvik class that are generally
+ * "inflated" into an in-memory representation at runtime. Instances of
+ * this class are represented in a compact streamable form in a
+ * <code>dex</code> file, as opposed to a random-access form.
+ */
+public final class ClassDataItem extends OffsettedItem {
+    /** non-null; what class this data is for, just for listing generation */
+    private final CstType thisClass;
+    
+    /** non-null; list of static fields */
+    private final ArrayList<EncodedField> staticFields;
+
+    /** non-null; list of initial values for static fields */
+    private final HashMap<EncodedField, Constant> staticValues;
+
+    /** non-null; list of instance fields */
+    private final ArrayList<EncodedField> instanceFields;
+
+    /** non-null; list of direct methods */
+    private final ArrayList<EncodedMethod> directMethods;
+
+    /** non-null; list of virtual methods */
+    private final ArrayList<EncodedMethod> virtualMethods;
+
+    /** null-ok; static initializer list; set in {@link #addContents} */
+    private CstArray staticValuesConstant;
+
+    /**
+     * null-ok; encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Constructs an instance. Its sets of members are initially
+     * empty.
+     * 
+     * @param thisClass non-null; what class this data is for, just
+     * for listing generation
+     */
+    public ClassDataItem(CstType thisClass) {
+        super(1, -1);
+
+        if (thisClass == null) {
+            throw new NullPointerException("thisClass == null");
+        }
+
+        this.thisClass = thisClass;
+        this.staticFields = new ArrayList<EncodedField>(20);
+        this.staticValues = new HashMap<EncodedField, Constant>(40);
+        this.instanceFields = new ArrayList<EncodedField>(20);
+        this.directMethods = new ArrayList<EncodedMethod>(20);
+        this.virtualMethods = new ArrayList<EncodedMethod>(20);
+        this.staticValuesConstant = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CLASS_DATA_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return toString();
+    }
+
+    /**
+     * Returns whether this instance is empty.
+     * 
+     * @return <code>true</code> if this instance is empty or
+     * <code>false</code> if at least one element has been added to it
+     */
+    public boolean isEmpty() {
+        return staticFields.isEmpty() && instanceFields.isEmpty()
+            && directMethods.isEmpty() && virtualMethods.isEmpty();
+    }
+
+    /**
+     * Adds a static field.
+     * 
+     * @param field non-null; the field to add
+     * @param value null-ok; initial value for the field, if any
+     */
+    public void addStaticField(EncodedField field, Constant value) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        if (staticValuesConstant != null) {
+            throw new UnsupportedOperationException(
+                    "static fields already sorted");
+        }
+
+        staticFields.add(field);
+        staticValues.put(field, value);
+    }
+
+    /**
+     * Adds an instance field.
+     * 
+     * @param field non-null; the field to add
+     */
+    public void addInstanceField(EncodedField field) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        instanceFields.add(field);
+    }
+
+    /**
+     * Adds a direct (<code>static</code> and/or <code>private</code>) method.
+     * 
+     * @param method non-null; the method to add
+     */
+    public void addDirectMethod(EncodedMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        directMethods.add(method);
+    }
+
+    /**
+     * Adds a virtual method.
+     * 
+     * @param method non-null; the method to add
+     */
+    public void addVirtualMethod(EncodedMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        virtualMethods.add(method);
+    }
+
+    /**
+     * Gets all the methods in this class. The returned list is not linked
+     * in any way to the underlying lists contained in this instance, but
+     * the objects contained in the list are shared.
+     * 
+     * @return non-null; list of all methods
+     */
+    public ArrayList<EncodedMethod> getMethods() {
+        int sz = directMethods.size() + virtualMethods.size();
+        ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz);
+
+        result.addAll(directMethods);
+        result.addAll(virtualMethods);
+
+        return result;
+    }
+
+
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way.
+     * 
+     * @param out non-null; where to output to
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(Writer out, boolean verbose) {
+        PrintWriter pw = Writers.printWriterFor(out);
+
+        int sz = staticFields.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  sfields[" + i + "]: " + staticFields.get(i));
+        }
+
+        sz = instanceFields.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  ifields[" + i + "]: " + instanceFields.get(i));
+        }
+
+        sz = directMethods.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  dmeths[" + i + "]:");
+            directMethods.get(i).debugPrint(pw, verbose);
+        }
+
+        sz = virtualMethods.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  vmeths[" + i + "]:");
+            virtualMethods.get(i).debugPrint(pw, verbose);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        if (!staticFields.isEmpty()) {
+            getStaticValuesConstant(); // Force the fields to be sorted.
+            for (EncodedField field : staticFields) {
+                field.addContents(file);
+            }
+        }
+
+        if (!instanceFields.isEmpty()) {
+            Collections.sort(instanceFields);
+            for (EncodedField field : instanceFields) {
+                field.addContents(file);
+            }
+        }
+
+        if (!directMethods.isEmpty()) {
+            Collections.sort(directMethods);
+            for (EncodedMethod method : directMethods) {
+                method.addContents(file);
+            }
+        }
+
+        if (!virtualMethods.isEmpty()) {
+            Collections.sort(virtualMethods);
+            for (EncodedMethod method : virtualMethods) {
+                method.addContents(file);
+            }
+        }
+    }
+
+    /**
+     * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+     * it contains any non-zero non-<code>null</code> values.
+     * 
+     * @return null-ok; the corresponding constant or <code>null</code> if
+     * there are no values to encode
+     */
+    public CstArray getStaticValuesConstant() {
+        if ((staticValuesConstant == null) && (staticFields.size() != 0)) {
+            staticValuesConstant = makeStaticValuesConstant();
+        }
+
+        return staticValuesConstant;
+    }
+
+    /**
+     * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+     * it contains any non-zero non-<code>null</code> values.
+     * 
+     * @return null-ok; the corresponding constant or <code>null</code> if
+     * there are no values to encode
+     */
+    private CstArray makeStaticValuesConstant() {
+        // First sort the statics into their final order.
+        Collections.sort(staticFields);
+        
+        /*
+         * Get the size of staticValues minus any trailing zeros/nulls (both
+         * nulls per se as well as instances of CstKnownNull).
+         */
+
+        int size = staticFields.size();
+        while (size > 0) {
+            EncodedField field = staticFields.get(size - 1);
+            Constant cst = staticValues.get(field);
+            if (cst instanceof CstLiteralBits) {
+                // Note: CstKnownNull extends CstLiteralBits.
+                if (((CstLiteralBits) cst).getLongBits() != 0) {
+                    break;
+                }
+            } else if (cst != null) {
+                break;
+            }
+            size--;
+        }
+
+        if (size == 0) {
+            return null;
+        }
+        
+        // There is something worth encoding, so build up a result.
+        
+        CstArray.List list = new CstArray.List(size);
+        for (int i = 0; i < size; i++) {
+            EncodedField field = staticFields.get(i);
+            Constant cst = staticValues.get(field);
+            if (cst == null) {
+                cst = Zeroes.zeroFor(field.getRef().getType());
+            }
+            list.set(i, cst);
+        }
+        list.setImmutable();
+
+        return new CstArray(list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+        encodeOutput(addedTo.getFile(), out);
+        encodedForm = out.toByteArray();
+        setWriteSize(encodedForm.length);
+    }
+
+    /**
+     * Writes out the encoded form of this instance.
+     * 
+     * @param file non-null; file this instance is part of
+     * @param out non-null; where to write to
+     */
+    private void encodeOutput(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int svSize = (staticValuesConstant == null) ? 0 :
+            staticValuesConstant.getList().size();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " class data for " +
+                    thisClass.toHuman());
+        }
+
+        encodeSize(file, out, "static_fields", staticFields.size());
+        encodeSize(file, out, "instance_fields", instanceFields.size());
+        encodeSize(file, out, "direct_methods", directMethods.size());
+        encodeSize(file, out, "virtual_methods", virtualMethods.size());
+
+        encodeList(file, out, "static_fields", staticFields);
+        encodeList(file, out, "instance_fields", instanceFields);
+        encodeList(file, out, "direct_methods", directMethods);
+        encodeList(file, out, "virtual_methods", virtualMethods);
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+    
+    /**
+     * Helper for {@link #encodeOutput}, which writes out the given
+     * size value, annotating it as well (if annotations are enabled).
+     * 
+     * @param file non-null; file this instance is part of
+     * @param out non-null; where to write to
+     * @param label non-null; the label for the purposes of annotation
+     * @param size &gt;= 0; the size to write
+     */
+    private static void encodeSize(DexFile file, AnnotatedOutput out,
+            String label, int size) {
+        if (out.annotates()) {
+            out.annotate(String.format("  %-21s %08x", label + "_size:",
+                            size));
+        }
+
+        out.writeUnsignedLeb128(size);
+    }
+
+    /**
+     * Helper for {@link #encodeOutput}, which writes out the given
+     * list. It also annotates the items (if any and if annotations
+     * are enabled).
+     * 
+     * @param file non-null; file this instance is part of
+     * @param out non-null; where to write to
+     * @param label non-null; the label for the purposes of annotation
+     * @param list non-null; the list in question
+     */
+    private static void encodeList(DexFile file, AnnotatedOutput out,
+            String label, ArrayList<? extends EncodedMember> list) {
+        int size = list.size();
+        int lastIndex = 0;
+
+        if (size == 0) {
+            return;
+        }
+        
+        if (out.annotates()) {
+            out.annotate(0, "  " + label + ":");
+        }
+
+        for (int i = 0; i < size; i++) {
+            lastIndex = list.get(i).encode(file, out, lastIndex, i);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+
+        if (annotates) {
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            encodeOutput(file, out);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefItem.java b/dx/src/com/android/dx/dex/file/ClassDefItem.java
new file mode 100644
index 0000000..5a0b27c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefItem.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.TreeSet;
+
+/**
+ * Representation of a Dalvik class, which is basically a set of
+ * members (fields or methods) along with a few more pieces of
+ * information.
+ */
+public final class ClassDefItem extends IndexedItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 32;
+
+    /** non-null; type constant for this class */
+    private final CstType thisClass;
+
+    /** access flags */
+    private final int accessFlags;
+
+    /**
+     * null-ok; superclass or <code>null</code> if this class is a/the
+     * root class 
+     */
+    private final CstType superclass;
+
+    /** null-ok; list of implemented interfaces */
+    private TypeListItem interfaces;
+
+    /** null-ok; source file name or <code>null</code> if unknown */
+    private final CstUtf8 sourceFile;
+
+    /** non-null; associated class data object */
+    private final ClassDataItem classData;
+
+    /**
+     * null-ok; item wrapper for the static values, initialized
+     * in {@link #addContents}
+     */
+    private EncodedArrayItem staticValuesItem;
+
+    /** non-null; annotations directory */
+    private AnnotationsDirectoryItem annotationsDirectory;
+
+    /**
+     * Constructs an instance. Its sets of members and annotations are
+     * initially empty.
+     * 
+     * @param thisClass non-null; type constant for this class
+     * @param accessFlags access flags
+     * @param superclass null-ok; superclass or <code>null</code> if
+     * this class is a/the root class
+     * @param interfaces non-null; list of implemented interfaces
+     * @param sourceFile null-ok; source file name or
+     * <code>null</code> if unknown
+     */
+    public ClassDefItem(CstType thisClass, int accessFlags,
+            CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
+        if (thisClass == null) {
+            throw new NullPointerException("thisClass == null");
+        }
+
+        /*
+         * TODO: Maybe check accessFlags and superclass, at
+         * least for easily-checked stuff?
+         */
+
+        if (interfaces == null) {
+            throw new NullPointerException("interfaces == null");
+        }
+
+        this.thisClass = thisClass;
+        this.accessFlags = accessFlags;
+        this.superclass = superclass;
+        this.interfaces = 
+            (interfaces.size() == 0) ? null :  new TypeListItem(interfaces);
+        this.sourceFile = sourceFile;
+        this.classData = new ClassDataItem(thisClass);
+        this.staticValuesItem = null;
+        this.annotationsDirectory = new AnnotationsDirectoryItem();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CLASS_DEF_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        MixedItemSection byteData = file.getByteData();
+        MixedItemSection wordData = file.getWordData();
+        MixedItemSection typeLists = file.getTypeLists();
+        StringIdsSection stringIds = file.getStringIds();
+
+        typeIds.intern(thisClass);
+
+        if (!classData.isEmpty()) {
+            MixedItemSection classDataSection = file.getClassData();
+            classDataSection.add(classData);
+
+            CstArray staticValues = classData.getStaticValuesConstant();
+            if (staticValues != null) {
+                staticValuesItem =
+                    byteData.intern(new EncodedArrayItem(staticValues));
+            }
+        }
+
+        if (superclass != null) {
+            typeIds.intern(superclass);
+        }
+
+        if (interfaces != null) {
+            interfaces = typeLists.intern(interfaces);
+        }
+
+        if (sourceFile != null) {
+            stringIds.intern(sourceFile);
+        }
+
+        if (! annotationsDirectory.isEmpty()) {
+            if (annotationsDirectory.isInternable()) {
+                annotationsDirectory = wordData.intern(annotationsDirectory);
+            } else {
+                wordData.add(annotationsDirectory);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        TypeIdsSection typeIds = file.getTypeIds();
+        int classIdx = typeIds.indexOf(thisClass);
+        int superIdx = (superclass == null) ? -1 :
+            typeIds.indexOf(superclass);
+        int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
+        int annoOff = annotationsDirectory.isEmpty() ? 0 :
+            annotationsDirectory.getAbsoluteOffset();
+        int sourceFileIdx = (sourceFile == null) ? -1 :
+            file.getStringIds().indexOf(sourceFile);
+        int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
+        int staticValuesOff =
+            OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
+
+        if (annotates) {
+            out.annotate(0, indexString() + ' ' + thisClass.toHuman());
+            out.annotate(4, "  class_idx:           " + Hex.u4(classIdx));
+            out.annotate(4, "  access_flags:        " + 
+                         AccessFlags.classString(accessFlags));
+            out.annotate(4, "  superclass_idx:      " + Hex.u4(superIdx) +
+                         " // " + ((superclass == null) ? "<none>" :
+                          superclass.toHuman()));
+            out.annotate(4, "  interfaces_off:      " + Hex.u4(interOff));
+            if (interOff != 0) {
+                TypeList list = interfaces.getList();
+                int sz = list.size();
+                for (int i = 0; i < sz; i++) {
+                    out.annotate(0, "    " + list.getType(i).toHuman());
+                }
+            }
+            out.annotate(4, "  source_file_idx:     " + Hex.u4(sourceFileIdx) +
+                         " // " + ((sourceFile == null) ? "<none>" :
+                          sourceFile.toHuman()));
+            out.annotate(4, "  annotations_off:     " + Hex.u4(annoOff));
+            out.annotate(4, "  class_data_off:      " + Hex.u4(dataOff));
+            out.annotate(4, "  static_values_off:   " +
+                    Hex.u4(staticValuesOff));
+        }
+
+        out.writeInt(classIdx);
+        out.writeInt(accessFlags);
+        out.writeInt(superIdx);
+        out.writeInt(interOff);
+        out.writeInt(sourceFileIdx);
+        out.writeInt(annoOff);
+        out.writeInt(dataOff);
+        out.writeInt(staticValuesOff);
+    }
+
+    /**
+     * Gets the constant corresponding to this class.
+     * 
+     * @return non-null; the constant
+     */
+    public CstType getThisClass() {
+        return thisClass;
+    }
+
+    /**
+     * Gets the access flags.
+     * 
+     * @return the access flags
+     */
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /**
+     * Gets the superclass.
+     * 
+     * @return null-ok; the superclass or <code>null</code> if
+     * this class is a/the root class
+     */
+    public CstType getSuperclass() {
+        return superclass;
+    }
+
+    /**
+     * Gets the list of interfaces implemented.
+     * 
+     * @return non-null; the interfaces list
+     */
+    public TypeList getInterfaces() {
+        if (interfaces == null) {
+            return StdTypeList.EMPTY;
+        }
+
+        return interfaces.getList();
+    }
+
+    /**
+     * Gets the source file name.
+     * 
+     * @return null-ok; the source file name or <code>null</code> if unknown
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+
+    /**
+     * Adds a static field.
+     * 
+     * @param field non-null; the field to add
+     * @param value null-ok; initial value for the field, if any
+     */
+    public void addStaticField(EncodedField field, Constant value) {
+        classData.addStaticField(field, value);
+    }
+
+    /**
+     * Adds an instance field.
+     * 
+     * @param field non-null; the field to add
+     */
+    public void addInstanceField(EncodedField field) {
+        classData.addInstanceField(field);
+    }
+
+    /**
+     * Adds a direct (<code>static</code> and/or <code>private</code>) method.
+     * 
+     * @param method non-null; the method to add
+     */
+    public void addDirectMethod(EncodedMethod method) {
+        classData.addDirectMethod(method);
+    }
+
+    /**
+     * Adds a virtual method.
+     * 
+     * @param method non-null; the method to add
+     */
+    public void addVirtualMethod(EncodedMethod method) {
+        classData.addVirtualMethod(method);
+    }
+
+    /**
+     * Gets all the methods in this class. The returned list is not linked
+     * in any way to the underlying lists contained in this instance, but
+     * the objects contained in the list are shared.
+     * 
+     * @return non-null; list of all methods
+     */
+    public ArrayList<EncodedMethod> getMethods() {
+        return classData.getMethods();
+    }
+
+    /**
+     * Sets the direct annotations on this class. These are annotations
+     * made on the class, per se, as opposed to on one of its members.
+     * It is only valid to call this method at most once per instance.
+     * 
+     * @param annotations non-null; annotations to set for this class
+     */
+    public void setClassAnnotations(Annotations annotations) {
+        annotationsDirectory.setClassAnnotations(annotations);
+    }
+
+    /**
+     * Adds a field annotations item to this class.
+     * 
+     * @param field non-null; field in question
+     * @param annotations non-null; associated annotations to add
+     */
+    public void addFieldAnnotations(CstFieldRef field,
+            Annotations annotations) {
+        annotationsDirectory.addFieldAnnotations(field, annotations);
+    }
+
+    /**
+     * Adds a method annotations item to this class.
+     * 
+     * @param method non-null; method in question
+     * @param annotations non-null; associated annotations to add
+     */
+    public void addMethodAnnotations(CstMethodRef method,
+            Annotations annotations) {
+        annotationsDirectory.addMethodAnnotations(method, annotations);
+    }
+
+    /**
+     * Adds a parameter annotations item to this class.
+     * 
+     * @param method non-null; method in question
+     * @param list non-null; associated list of annotation sets to add
+     */
+    public void addParameterAnnotations(CstMethodRef method,
+            AnnotationsList list) {
+        annotationsDirectory.addParameterAnnotations(method, list);
+    }
+
+    /**
+     * Gets the method annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     * 
+     * @param method non-null; the method
+     * @return null-ok; the method annotations, if any
+     */
+    public Annotations getMethodAnnotations(CstMethodRef method) {
+        return annotationsDirectory.getMethodAnnotations(method);
+    }
+
+    /**
+     * Gets the parameter annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     * 
+     * @param method non-null; the method
+     * @return null-ok; the parameter annotations, if any
+     */
+    public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+        return annotationsDirectory.getParameterAnnotations(method);
+    }
+    
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way.
+     * 
+     * @param out non-null; where to output to
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(Writer out, boolean verbose) {
+        PrintWriter pw = Writers.printWriterFor(out);
+
+        pw.println(getClass().getName() + " {");
+        pw.println("  accessFlags: " + Hex.u2(accessFlags));
+        pw.println("  superclass: " + superclass);
+        pw.println("  interfaces: " +
+                ((interfaces == null) ? "<none>" : interfaces));
+        pw.println("  sourceFile: " +
+                ((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
+
+        classData.debugPrint(out, verbose);
+        annotationsDirectory.debugPrint(pw);
+        
+        pw.println("}");
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefsSection.java b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
new file mode 100644
index 0000000..cf61b47
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Class definitions list section of a <code>.dex</code> file.
+ */
+public final class ClassDefsSection extends UniformItemSection {
+    /**
+     * non-null; map from type constants for classes to {@link
+     * ClassDefItem} instances that define those classes
+     */
+    private final TreeMap<Type, ClassDefItem> classDefs;
+
+    /** null-ok; ordered list of classes; set in {@link #orderItems} */
+    private ArrayList<ClassDefItem> orderedDefs;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public ClassDefsSection(DexFile file) {
+        super("class_defs", file, 4);
+
+        classDefs = new TreeMap<Type, ClassDefItem>();
+        orderedDefs = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        if (orderedDefs != null) {
+            return orderedDefs;
+        }
+        
+        return classDefs.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        Type type = ((CstType) cst).getClassType();
+        IndexedItem result = classDefs.get(type);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = classDefs.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "class_defs_size: " + Hex.u4(sz));
+            out.annotate(4, "class_defs_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Adds an element to this instance. It is illegal to attempt to add more
+     * than one class with the same name.
+     * 
+     * @param clazz non-null; the class def to add
+     */
+    public void add(ClassDefItem clazz) {
+        Type type;
+
+        try {
+            type = clazz.getThisClass().getClassType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("clazz == null");
+        }
+
+        throwIfPrepared();
+
+        if (classDefs.get(type) != null) {
+            throw new IllegalArgumentException("already added: " + type);
+        }
+
+        classDefs.put(type, clazz);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int sz = classDefs.size();
+        int idx = 0;
+
+        orderedDefs = new ArrayList<ClassDefItem>(sz);
+
+        /*
+         * Iterate over all the classes, recursively assigning an
+         * index to each, implicitly skipping the ones that have
+         * already been assigned by the time this (top-level)
+         * iteration reaches them.
+         */
+        for (Type type : classDefs.keySet()) {
+            idx = orderItems0(type, idx, sz - idx);
+        }
+    }
+
+    /**
+     * Helper for {@link #orderItems}, which recursively assigns indices
+     * to classes.
+     * 
+     * @param type null-ok; type ref to assign, if any
+     * @param idx &gt;= 0; the next index to assign
+     * @param maxDepth maximum recursion depth; if negative, this will
+     * throw an exception indicating class definition circularity
+     * @return &gt;= 0; the next index to assign
+     */
+    private int orderItems0(Type type, int idx, int maxDepth) {
+        ClassDefItem c = classDefs.get(type);
+
+        if ((c == null) || (c.hasIndex())) {
+            return idx;
+        }
+
+        if (maxDepth < 0) {
+            throw new RuntimeException("class circularity with " + type);
+        }
+
+        maxDepth--;
+
+        CstType superclassCst = c.getSuperclass();
+        if (superclassCst != null) {
+            Type superclass = superclassCst.getClassType();
+            idx = orderItems0(superclass, idx, maxDepth);
+        }
+
+        TypeList interfaces = c.getInterfaces();
+        int sz = interfaces.size();
+        for (int i = 0; i < sz; i++) {
+            idx = orderItems0(interfaces.getType(i), idx, maxDepth);
+        }
+
+        c.setIndex(idx);
+        orderedDefs.add(c);
+        return idx + 1;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/CodeItem.java b/dx/src/com/android/dx/dex/file/CodeItem.java
new file mode 100644
index 0000000..dc0bb52
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CodeItem.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+
+/**
+ * Representation of all the parts needed for concrete methods in a
+ * <code>dex</code> file.
+ */
+public final class CodeItem extends OffsettedItem {
+    /** file alignment of this class, in bytes */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of the header of this class, in bytes */
+    private static final int HEADER_SIZE = 16;
+
+    /** non-null; method that this code implements */
+    private final CstMethodRef ref;
+
+    /** non-null; the bytecode instructions and associated data */
+    private final DalvCode code;
+
+    /** null-ok; the catches, if needed; set in {@link #addContents} */
+    private CatchStructs catches;
+
+    /** whether this instance is for a <code>static</code> method */
+    private final boolean isStatic;
+
+    /**
+     * non-null; list of possibly-thrown exceptions; just used in
+     * generating debugging output (listings) 
+     */
+    private final TypeList throwsList;
+
+    /**
+     * null-ok; the debug info or <code>null</code> if there is none;
+     * set in {@link #addContents}
+     */
+    private DebugInfoItem debugInfo;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param ref non-null; method that this code implements
+     * @param code non-null; the underlying code
+     * @param isStatic whether this instance is for a <code>static</code>
+     * method
+     * @param throwsList non-null; list of possibly-thrown exceptions,
+     * just used in generating debugging output (listings)
+     */
+    public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic,
+            TypeList throwsList) {
+        super(ALIGNMENT, -1);
+
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        if (throwsList == null) {
+            throw new NullPointerException("throwsList == null");
+        }
+
+        this.ref = ref;
+        this.code = code;
+        this.isStatic = isStatic;
+        this.throwsList = throwsList;
+        this.catches = null;
+        this.debugInfo = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CODE_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection wordData = file.getWordData();
+        MixedItemSection byteData = file.getByteData();
+        TypeIdsSection typeIds = file.getTypeIds();
+
+        if (code.hasPositions() || code.hasLocals()) {
+            debugInfo = new DebugInfoItem(code, isStatic, ref);
+            byteData.add(debugInfo);
+        }
+
+        if (code.hasAnyCatches()) {
+            for (Type type : code.getCatchTypes()) {
+                typeIds.intern(type);
+            }
+            catches = new CatchStructs(code);
+        }
+
+        for (Constant c : code.getInsnConstants()) {
+            file.internIfAppropriate(c);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "CodeItem{" + toHuman() + "}";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return ref.toHuman();
+    }
+
+    /**
+     * Gets the reference to the method this instance implements.
+     * 
+     * @return non-null; the method reference
+     */
+    public CstMethodRef getRef() {
+        return ref;
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     * 
+     * @param out non-null; where to dump
+     * @param prefix non-null; per-line prefix to use
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(PrintWriter out, String prefix, boolean verbose) {
+        out.println(ref.toHuman() + ":");
+
+        DalvInsnList insns = code.getInsns();
+        out.println("regs: " + Hex.u2(getRegistersSize()) +
+                "; ins: " + Hex.u2(getInsSize()) + "; outs: " +
+                Hex.u2(getOutsSize()));
+
+        insns.debugPrint(out, prefix, verbose);
+
+        String prefix2 = prefix + "  ";
+
+        if (catches != null) {
+            out.print(prefix);
+            out.println("catches");
+            catches.debugPrint(out, prefix2);
+        }
+
+        if (debugInfo != null) {
+            out.print(prefix);
+            out.println("debug info");
+            debugInfo.debugPrint(out, prefix2);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        final DexFile file = addedTo.getFile();
+        int catchesSize;
+        
+        /*
+         * In order to get the catches and insns, all the code's
+         * constants need to be assigned indices.
+         */
+        code.assignIndices(new DalvCode.AssignIndicesCallback() {
+                public int getIndex(Constant cst) {
+                    IndexedItem item = file.findItemOrNull(cst);
+                    if (item == null) {
+                        return -1;
+                    }
+                    return item.getIndex();
+                }
+            });
+
+        if (catches != null) {
+            catches.encode(file);
+            catchesSize = catches.writeSize();
+        } else {
+            catchesSize = 0;
+        }
+
+        /*
+         * The write size includes the header, two bytes per code
+         * unit, post-code padding if necessary, and however much
+         * space the catches need.
+         */
+
+        int insnsSize = code.getInsns().codeSize();
+        if ((insnsSize & 1) != 0) {
+            insnsSize++;
+        }
+
+        setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int regSz = getRegistersSize();
+        int outsSz = getOutsSize();
+        int insSz = getInsSize();
+        int insnsSz = code.getInsns().codeSize();
+        boolean needPadding = (insnsSz & 1) != 0;
+        int triesSz = (catches == null) ? 0 : catches.triesSize();
+        int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + ' ' + ref.toHuman());
+            out.annotate(2, "  registers_size: " + Hex.u2(regSz));
+            out.annotate(2, "  ins_size:       " + Hex.u2(insSz));
+            out.annotate(2, "  outs_size:      " + Hex.u2(outsSz));
+            out.annotate(2, "  tries_size:     " + Hex.u2(triesSz));
+            out.annotate(4, "  debug_off:      " + Hex.u4(debugOff));
+            out.annotate(4, "  insns_size:     " + Hex.u4(insnsSz));
+
+            // This isn't represented directly here, but it is useful to see.
+            int size = throwsList.size();
+            if (size != 0) {
+                out.annotate(0, "  throws " + StdTypeList.toHuman(throwsList));
+            }
+        }
+
+        out.writeShort(regSz);
+        out.writeShort(insSz);
+        out.writeShort(outsSz);
+        out.writeShort(triesSz);
+        out.writeInt(debugOff);
+        out.writeInt(insnsSz);
+
+        writeCodes(file, out);
+
+        if (catches != null) {
+            if (needPadding) {
+                if (annotates) {
+                    out.annotate(2, "  padding: 0");
+                }
+                out.writeShort(0);
+            }
+
+            catches.writeTo(file, out);
+        }
+
+        if (annotates) {
+            /*
+             * These are pointed at in the code header (above), but it's less
+             * distracting to expand on them at the bottom of the code.
+             */
+            if (debugInfo != null) {
+                out.annotate(0, "  debug info");
+                debugInfo.annotateTo(file, out, "    ");
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #writeTo0} which writes out the actual bytecode.
+     *
+     * @param file non-null; file we are part of
+     * @param out non-null; where to write to
+     */
+    private void writeCodes(DexFile file, AnnotatedOutput out) {
+        DalvInsnList insns = code.getInsns();
+
+        try {
+            insns.writeTo(out);
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex, "...while writing " +
+                    "instructions for " + ref.toHuman());
+        }
+    }
+
+    /**
+     * Get the in registers count.
+     * 
+     * @return the count
+     */
+    private int getInsSize() {
+        return ref.getParameterWordCount(isStatic);
+    }
+
+    /**
+     * Get the out registers count.
+     * 
+     * @return the count
+     */
+    private int getOutsSize() {
+        return code.getInsns().getOutsSize();
+    }
+
+    /**
+     * Get the total registers count.
+     * 
+     * @return the count
+     */
+    private int getRegistersSize() {
+        return code.getInsns().getRegistersSize();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoConstants.java b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
new file mode 100644
index 0000000..010acb4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Constants for the dex debug info state machine format.
+ */
+public interface DebugInfoConstants {
+
+    /*
+     * normal opcodes
+     */
+
+    /**
+     * Terminates a debug info sequence for a method.<p>
+     * Args: none
+     *
+     */
+    static final int DBG_END_SEQUENCE = 0x00;
+
+    /**
+     * Advances the program counter/address register without emitting
+     * a positions entry.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; amount to advance pc by
+     * </ol>
+     */
+    static final int DBG_ADVANCE_PC = 0x01;
+
+    /**
+     * Advances the line register without emitting
+     * a positions entry.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Signed LEB128 &mdash; amount to change line register by.
+     * </ol>
+     */
+    static final int DBG_ADVANCE_LINE = 0x02;
+
+    /**
+     * Introduces a local variable at the current address.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that will contain local.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+     * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+     * </ol>
+     */
+    static final int DBG_START_LOCAL = 0x03;
+
+    /**
+     * Introduces a local variable at the current address with a type
+     * signature specified.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that will contain local.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+     * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of
+     * type signature.
+     * </ol>
+     */
+    static final int DBG_START_LOCAL_EXTENDED = 0x04;
+
+    /**
+     * Marks a currently-live local variable as out of scope at the
+     * current address.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that contained local
+     * </ol>
+     */
+    static final int DBG_END_LOCAL = 0x05;
+
+    /**
+     * Re-introduces a local variable at the current address. The name
+     * and type are the same as the last local that was live in the specified
+     * register.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register to re-start.
+     * </ol>
+     */
+    static final int DBG_RESTART_LOCAL = 0x06;
+
+
+    /**
+     * Sets the "prologue_end" state machine register, indicating that the
+     * next position entry that is added should be considered the end of
+     * a method prologue (an appropriate place for a method breakpoint).<p>
+     *
+     * The prologue_end register is cleared by any special (&gt= OPCODE_BASE)
+     * opcode.
+     */
+    static final int DBG_SET_PROLOGUE_END = 0x07;
+
+    /**
+     * Sets the "epilogue_begin" state machine register, indicating that the
+     * next position entry that is added should be considered the beginning of
+     * a method epilogue (an appropriate place to suspend execution before
+     * method exit).<p>
+     *
+     * The epilogue_begin register is cleared by any special (&gt= OPCODE_BASE)
+     * opcode.
+     */
+    static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
+
+    /**
+     * Sets the current file that that line numbers refer to. All subsequent
+     * line number entries make reference to this source file name, instead
+     * of the default name specified in code_item.
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of source
+     * file name.
+     * </ol>
+     */
+    static final int DBG_SET_FILE = 0x09;
+
+    /* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */
+
+    /*
+     * "special opcode" configuration, essentially what's found in
+     * the line number program header in DWARFv3, Section 6.2.4
+     */
+
+    /** the smallest value a special opcode can take */
+    static final int DBG_FIRST_SPECIAL = 0x0a;
+    static final int DBG_LINE_BASE = -4;
+    static final int DBG_LINE_RANGE = 15;
+    // MIN_INSN_LENGTH is always 1
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
new file mode 100644
index 0000000..f630f1c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * A decoder for the dex debug info state machine format.
+ * This code exists mostly as a reference implementation and test for
+ * for the <code>DebugInfoEncoder</code>
+ */
+public class DebugInfoDecoder {
+    /** encoded debug info */
+    private final byte[] encoded;
+    /** positions decoded */
+    private final ArrayList<PositionEntry> positions;
+    /** locals decoded */
+    private final ArrayList<LocalEntry> locals;
+    /** size of code block in code units */
+    private final int codesize;
+    /** indexed by register, the last local variable live in a reg */
+    private final LocalEntry[] lastEntryForReg;
+    /** method descriptor of method this debug info is for */
+    private final Prototype desc;
+    /** true if method is static */
+    private final boolean isStatic;
+    /** dex file this debug info will be stored in */
+    private final DexFile file;
+    /**
+     * register size, in register units, of the register space
+     * used by this method
+     */
+    private final int regSize;
+
+    /** current decoding state: line number */
+    private int line = 1;
+    
+    /** current decoding state: bytecode address */
+    private int address = 0;
+
+    /** string index of the string "this" */
+    private final int thisStringIdx;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param encoded encoded debug info
+     * @param codesize size of code block in code units
+     * @param regSize register size, in register units, of the register space
+     * used by this method
+     * @param isStatic true if method is static
+     * @param ref method descriptor of method this debug info is for
+     * @param file dex file this debug info will be stored in
+     */
+    DebugInfoDecoder (byte[] encoded, int codesize, int regSize,
+            boolean isStatic, CstMethodRef ref, DexFile file) {
+
+        this.encoded = encoded;
+        this.isStatic = isStatic;
+        this.desc = ref.getPrototype();
+        this.file = file;
+        this.regSize = regSize;
+        
+        positions = new ArrayList();
+        locals = new ArrayList();
+        this.codesize = codesize;
+        lastEntryForReg = new LocalEntry[regSize];
+
+        thisStringIdx = file.getStringIds().indexOf(new CstUtf8("this"));
+    }
+
+    /**
+     * An entry in the resulting postions table
+     */
+    static class PositionEntry {
+        /** bytecode address */
+        int address;
+        /** line number */
+        int line;
+
+        PositionEntry(int address, int line) {
+            this.address = address;
+            this.line = line;
+        }
+    }
+
+    /**
+     * An entry in the resulting locals table
+     */
+    static class LocalEntry {
+        LocalEntry(int start, int reg, int nameIndex, int typeIndex,
+                int signatureIndex) {
+            this.start          = start;
+            this.reg            = reg;
+            this.nameIndex      = nameIndex;
+            this.typeIndex      = typeIndex;
+            this.signatureIndex = signatureIndex;
+        }
+
+        /** start of address range */
+        int start;
+
+        /**
+         * End of address range. Initialized to MAX_VALUE here but will
+         * be set to no more than 1 + max bytecode address of method.
+         */
+        int end = Integer.MAX_VALUE;
+
+        /** register number */
+        int reg;
+
+        /** index of name in strings table */
+        int nameIndex;
+
+        /** index of type in types table */
+        int typeIndex;
+
+        /** index of type signature in strings table */
+        int signatureIndex;
+
+    }
+
+    /**
+     * Gets the decoded positions list.
+     * Valid after calling <code>decode</code>.
+     *
+     * @return positions list in ascending address order.
+     */
+    public List<PositionEntry> getPositionList() {
+        return positions;
+    }
+
+    /**
+     * Gets the decoded locals list, in ascending start-address order.
+     * Valid after calling <code>decode</code>.
+     *
+     * @return locals list in ascending address order.
+     */
+    public List<LocalEntry> getLocals() {
+        // TODO move this loop:
+        // Any variable that didnt end ends now
+        for (LocalEntry local: locals) {
+            if (local.end == Integer.MAX_VALUE) {
+                local.end = codesize;
+            }
+        }
+        return locals;
+    }
+
+    /**
+     * Decodes the debug info sequence.
+     */
+    public void decode() {
+        try {
+            decode0();
+        } catch (Exception ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while decoding debug info");
+        }
+    }
+
+    /**
+     * Reads a string index. String indicies are offset by 1, and a 0 value
+     * in the stream (-1 as returned by this method) means "null"
+     *
+     * @param bs
+     * @return index into file's string ids table, -1 means null
+     * @throws IOException
+     */
+    private int readStringIndex(InputStream bs) throws IOException {
+        int offsetIndex = readUnsignedLeb128(bs);
+
+        return offsetIndex - 1;
+    }
+
+    /**
+     * Gets the register that begins the method's parameter range (including
+     * the 'this' parameter for non-static methods). The range continues until
+     * <code>regSize</code>
+     *
+     * @return register as noted above.
+     */
+    private int getParamBase() {
+        return regSize
+                - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+    }
+
+    private void decode0() throws IOException {
+        ByteArrayInputStream bs = new ByteArrayInputStream(encoded);
+
+        line = readUnsignedLeb128(bs);
+        int szParams = readUnsignedLeb128(bs);
+        StdTypeList params = desc.getParameterTypes();
+        int curReg = getParamBase();
+
+        if (szParams != params.size()) {
+            throw new RuntimeException(
+                    "Mismatch between parameters_size and prototype");
+        }
+        
+        if (!isStatic) {
+            // Start off with implicit 'this' entry
+            LocalEntry thisEntry
+                    = new LocalEntry(0, curReg, thisStringIdx, 0, 0);
+            locals.add(thisEntry);
+            lastEntryForReg[curReg] = thisEntry;
+            curReg++;
+        }
+
+        for (int i = 0; i < szParams; i++) {
+            Type paramType = params.getType(i);
+            LocalEntry le;
+
+            int nameIdx = readStringIndex(bs);
+
+            if(nameIdx == -1) {
+                // unnamed parameter
+            } else {
+                // final '0' should be idx of paramType.getDescriptor()
+                le = new LocalEntry(0, curReg, nameIdx, 0, 0);
+                locals.add(le);
+                lastEntryForReg[curReg] = le;
+            }
+
+            curReg += paramType.getCategory();
+        }
+
+        for (;;) {
+            int opcode = bs.read();
+
+            if (opcode < 0) {
+                throw new RuntimeException
+                        ("Reached end of debug stream without "
+                                + "encountering end marker");
+            }
+
+            switch (opcode) {
+                case DBG_START_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    int nameIdx = readStringIndex(bs);
+                    int typeIdx = readStringIndex(bs);
+                    LocalEntry le = new LocalEntry(
+                            address, reg, nameIdx, typeIdx, 0);
+
+                    // a "start" is implicitly the "end" of whatever was
+                    // previously defined in the register
+                    if (lastEntryForReg[reg] != null
+                            && lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+
+                        lastEntryForReg[reg].end = address;
+                    }
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_START_LOCAL_EXTENDED: {
+                    int reg = readUnsignedLeb128(bs);
+                    int nameIdx = readStringIndex(bs);
+                    int typeIdx = readStringIndex(bs);
+                    int sigIdx = readStringIndex(bs);
+                    LocalEntry le = new LocalEntry(
+                            address, reg, nameIdx, typeIdx, sigIdx);
+
+                    // a "start" is implicitly the "end" of whatever was
+                    // previously defined in the register
+                    if (lastEntryForReg[reg] != null
+                            && lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+
+                        lastEntryForReg[reg].end = address;
+
+                        // A 0-length entry. Almost certainly a "this"
+                        // with a signature.
+                        if (lastEntryForReg[reg].start == address) {
+                            locals.remove(lastEntryForReg[reg]);
+                        }
+                    }
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_RESTART_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    LocalEntry prevle;
+                    LocalEntry le;
+
+                    try {
+                        prevle = lastEntryForReg[reg];
+
+                        if (lastEntryForReg[reg].end == Integer.MAX_VALUE) {
+                            throw new RuntimeException ("nonsensical "
+                                    + "RESTART_LOCAL on live register v"+reg);
+                        }
+                        le = new LocalEntry(address, reg,
+                                prevle.nameIndex, prevle.typeIndex, 0);
+
+                    } catch (NullPointerException ex) {
+                        throw new RuntimeException
+                                ("Encountered RESTART_LOCAL on new v" +reg);
+                    }
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_END_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    boolean found = false;
+                    for (int i = locals.size() - 1; i >= 0; i--) {
+                        if (locals.get(i).reg == reg) {
+                            locals.get(i).end = address;
+                            found = true;
+                            break;
+                        }
+                    }
+
+                    if (!found) {
+                        throw new RuntimeException(
+                                "Encountered LOCAL_END without local start: v"
+                                        + reg);
+                    }
+                }
+                break;
+
+                case DBG_END_SEQUENCE:
+                    // all done
+                return;
+
+                case DBG_ADVANCE_PC:
+                    address += readUnsignedLeb128(bs);
+                break;
+
+                case DBG_ADVANCE_LINE:
+                    line += readSignedLeb128(bs);
+                break;
+
+                case DBG_SET_PROLOGUE_END:
+                    //TODO do something with this.
+                break;
+
+                case DBG_SET_EPILOGUE_BEGIN:
+                    //TODO do something with this.
+                break;
+
+                case DBG_SET_FILE:
+                    //TODO do something with this.
+                break;
+
+                default:
+                    if (opcode < DBG_FIRST_SPECIAL) {
+                        throw new RuntimeException(
+                                "Invalid extended opcode encountered "
+                                        + opcode);
+                    }
+
+                    int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+                    address += adjopcode / DBG_LINE_RANGE;
+                    line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+                    positions.add(new PositionEntry(address, line));
+                break;
+
+            }
+        }
+    }
+
+    /**
+     * Validates an encoded debug info stream against data used to encode it,
+     * throwing an exception if they do not match. Used to validate the
+     * encoder.
+     *
+     * @param linecodes encoded debug info
+     * @param codeSize size of insn block in code units
+     * @param countRegisters size of used register block in register units
+     * @param pl position list to verify against
+     * @param ll locals list to verify against.
+     */
+    public static void validateEncode(byte[] linecodes, int codeSize,
+            int countRegisters, PositionList pl, LocalList ll,
+            boolean isStatic, CstMethodRef ref, DexFile file) {
+
+        try {
+            validateEncode0(linecodes, codeSize, countRegisters,
+                    isStatic, ref, file, pl, ll);
+        } catch (RuntimeException ex) {
+//            System.err.println(ex.toString()
+//                    + " while processing " + ref.toHuman());
+            throw ExceptionWithContext.withContext(ex,
+                    "while processing " + ref.toHuman());
+        }
+    }
+
+    
+    private static void validateEncode0(byte[] linecodes, int codeSize,
+            int countRegisters, boolean isStatic, CstMethodRef ref,
+            DexFile file, PositionList pl, LocalList ll) {
+        DebugInfoDecoder decoder
+                = new DebugInfoDecoder(linecodes, codeSize, countRegisters,
+                    isStatic, ref, file);
+
+        decoder.decode();
+
+        List<PositionEntry> decodedEntries;
+
+        decodedEntries = decoder.getPositionList();
+
+        if (decodedEntries.size() != pl.size()) {
+            throw new RuntimeException(
+                    "Decoded positions table not same size was "
+                    + decodedEntries.size() + " expected " + pl.size());
+        }
+
+        for (PositionEntry entry: decodedEntries) {
+            boolean found = false;
+            for (int i = pl.size() - 1; i >= 0; i--) {
+                PositionList.Entry ple = pl.get(i);
+
+                if (entry.line == ple.getPosition().getLine()
+                        && entry.address == ple.getAddress()) {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                throw new RuntimeException ("Could not match position entry: "
+                        + entry.address + ", " + entry.line);
+            }
+        }
+
+        List<LocalEntry> decodedLocals;
+
+        decodedLocals = decoder.getLocals();
+
+        int paramBase = decoder.getParamBase();
+
+        int matchedLocalsEntries = 0;
+
+        for (LocalEntry entry: decodedLocals) {
+            boolean found = false;
+            for (int i = ll.size() - 1; i >= 0; i--) {
+                LocalList.Entry le = ll.get(i);
+
+                /*
+                 * If an entry is a method parameter, then the original
+                 * entry may not be marked as starting at 0. However, the
+                 * end address shuld still match.
+                 */
+                if ((entry.start == le.getStart()
+                        || (entry.start == 0 && entry.reg >= paramBase))
+                        && entry.end == le.getEnd()
+                        && entry.reg == le.getRegister()) {
+                    found = true;
+                    matchedLocalsEntries++;
+                    break;
+                }
+            }
+
+            if (!found) {
+                throw new RuntimeException("Could not match local entry");
+            }
+        }
+
+        if (matchedLocalsEntries != ll.size()) {
+            throw new RuntimeException("Locals tables did not match");
+        }
+    }
+
+    /**
+     * Reads a DWARFv3-style signed LEB128 integer to the specified stream.
+     * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+     *
+     * @param bs stream to input from
+     * @return read value
+     * @throws IOException on invalid sequence in addition to
+     * those caused by the InputStream
+     */
+    public static int readSignedLeb128(InputStream bs) throws IOException {
+        int result = 0;
+        int cur;
+        int count = 0;
+        int signBits = -1;
+
+        do {
+            cur = bs.read();
+            result |= (cur & 0x7f) << (count * 7);
+            signBits <<= 7;
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new IOException ("invalid LEB128 sequence");
+        }
+
+        // Sign extend if appropriate
+        if (((signBits >> 1) & result) != 0 ) {
+            result |= signBits;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads a DWARFv3-style unsigned LEB128 integer to the specified stream.
+     * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+     *
+     * @param bs stream to input from
+     * @return read value, which should be treated as an unsigned value.
+     * @throws IOException on invalid sequence in addition to
+     * those caused by the InputStream
+     */
+    public static int readUnsignedLeb128(InputStream bs) throws IOException {
+        int result = 0;
+        int cur;
+        int count = 0;
+
+        do {
+            cur = bs.read();
+            result |= (cur & 0x7f) << (count * 7);
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new IOException ("invalid LEB128 sequence");
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
new file mode 100644
index 0000000..cf0fb23
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
@@ -0,0 +1,1044 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.BitSet;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * An encoder for the dex debug info state machine format. The format
+ * for each method enrty is as follows:
+ * <ol>
+ * <li> signed LEB128: initial value for line register.
+ * <li> n instances of signed LEB128: string indicies (offset by 1)
+ * for each method argument in left-to-right order
+ * with <code>this</code> excluded. A value of '0' indicates "no name"
+ * <li> A sequence of special or normal opcodes as defined in
+ * <code>DebugInfoConstants</code>.
+ * <li> A single terminating <code>OP_END_SEQUENCE</code>
+ * </ol>
+ */
+public final class DebugInfoEncoder {
+    private static final boolean DEBUG = false;
+
+    private final PositionList positionlist;
+    private final LocalList locallist;
+    private final ByteArrayAnnotatedOutput output;
+    private final DexFile file;
+    private final int codeSize;
+    private final int regSize;
+
+    private final Prototype desc;
+    private final boolean isStatic;
+
+    /** current encoding state: bytecode address */
+    private int address = 0;
+
+    /** current encoding state: line number */
+    private int line = 1;
+
+    /**
+     * if non-null: the output to write annotations to. No normal
+     * output is written to this.
+     */
+    private AnnotatedOutput annotateTo;
+
+    /** if non-null: another possible output for annotations */
+    private PrintWriter debugPrint;
+
+    /** if non-null: the prefix for each annotation or debugPrint line */
+    private String prefix;
+
+    /** true if output should be consumed during annotation */
+    private boolean shouldConsume;
+
+    /** indexed by register; last local alive in register */
+    private final LocalList.Entry[] lastEntryForReg;
+
+    /**
+     * Creates an instance.
+     *
+     * @param pl null-ok
+     * @param ll null-ok
+     * @param file null-ok; may only be <code>null</code> if simply using
+     * this class to do a debug print
+     * @param codeSize
+     * @param regSize
+     * @param isStatic
+     * @param ref
+     */
+    public DebugInfoEncoder(PositionList pl, LocalList ll,
+            DexFile file, int codeSize, int regSize,
+            boolean isStatic, CstMethodRef ref) {
+
+        this.positionlist = pl;
+        this.locallist = ll;
+        this.file = file;
+        output = new ByteArrayAnnotatedOutput();
+        this.desc = ref.getPrototype();
+        this.isStatic = isStatic;
+
+        this.codeSize = codeSize;
+        this.regSize = regSize;
+
+        lastEntryForReg = new LocalList.Entry[regSize];
+    }
+
+    /**
+     * Annotates or writes a message to the <code>debugPrint</code> writer
+     * if applicable.
+     *
+     * @param length the number of bytes associated with this message
+     * @param message the message itself
+     */
+    private void annotate(int length, String message) {
+        if (prefix != null) {
+            message = prefix + message;
+        }
+
+        if (annotateTo != null) {
+            annotateTo.annotate(shouldConsume ? length : 0, message);
+        }
+
+        if (debugPrint != null) {
+            debugPrint.println(message);
+        }
+    }
+
+    /**
+     * Converts this (PositionList, LocalList) pair into a state machine
+     * sequence.
+     *
+     * @return encoded byte sequence without padding and
+     * terminated with a <code>'\00'</code>
+     */
+    public byte[] convert() {
+        try {
+            byte[] ret;
+            ret = convert0();
+
+            if (DEBUG) {
+                for (int i = 0 ; i < ret.length; i++) {
+                    System.err.printf("byte %02x\n", (0xff & ret[i]));
+                }
+            }
+
+            return ret;
+        } catch (IOException ex) {
+            throw ExceptionWithContext
+                    .withContext(ex, "...while encoding debug info");
+        }
+    }
+
+    /**
+     * Converts and produces annotations on a stream. Does not write
+     * actual bits to the <code>AnnotatedOutput</code>.
+     *
+     * @param prefix null-ok; prefix to attach to each line of output
+     * @param debugPrint null-ok; if specified, an alternate output for
+     * annotations
+     * @param out null-ok; if specified, where annotations should go
+     * @param consume whether to claim to have consumed output for
+     * <code>out</code>
+     * @return output sequence
+     */
+    public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint,
+            AnnotatedOutput out, boolean consume) {
+        this.prefix = prefix;
+        this.debugPrint = debugPrint;
+        annotateTo = out;
+        shouldConsume = consume;
+
+        byte[] result = convert();
+
+        return result;
+    }
+    
+    private byte[] convert0() throws IOException {
+        ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
+        ArrayList<LocalList.Entry> sortedLocalsStart = buildLocalsStart();
+
+        // Parameter locals are removed from sortedLocalsStart here.
+        ArrayList<LocalList.Entry> methodArgs
+                = extractMethodArguments(sortedLocalsStart);
+
+        ArrayList<LocalList.Entry> sortedLocalsEnd
+                = buildLocalsEnd(sortedLocalsStart);
+
+        emitHeader(sortedPositions, methodArgs);
+
+        // TODO: Make this mark the actual prologue end.
+        output.writeByte(DBG_SET_PROLOGUE_END);
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1, String.format("%04x: prologue end",address));
+        }
+
+        int szp = sortedPositions.size();
+        int szl = sortedLocalsStart.size();
+
+        // Current index in sortedPositions
+        int curp = 0;
+        // Current index in sortedLocalsStart
+        int curls = 0;
+        // Current index in sortedLocalsEnd
+        int curle = 0;
+
+        for (;;) {
+            /*
+             * Emit any information for the current address.
+             */
+
+            curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls,
+                    sortedLocalsStart);
+
+            /*
+             * Our locals-sorted-by-range-end has reached the end
+             * of the code block. Ignore everything else.
+             */
+            if (address == codeSize) {
+                curle = szl;
+            }
+
+            curls = emitLocalStartsAtAddress(curls, sortedLocalsStart);
+
+            curp = emitPositionsAtAddress(curp, sortedPositions);
+
+            /*
+             * Figure out what the next important address is.
+             */
+
+            int nextAddrLS = Integer.MAX_VALUE;
+            int nextAddrLE = Integer.MAX_VALUE;
+            int nextAddrP = Integer.MAX_VALUE;
+
+            if (curls < szl) {
+                nextAddrLS = sortedLocalsStart.get(curls).getStart();
+            }
+
+            if (curle < szl) {
+                nextAddrLE = sortedLocalsEnd.get(curle).getEnd();
+            }
+
+            if (curp < szp) {
+                nextAddrP = sortedPositions.get(curp).getAddress();
+            }
+
+            int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE));
+
+            // No next important address == done.
+            if (next == Integer.MAX_VALUE) {
+                break;
+            }
+
+            /*
+             * If the only work remaining are local ends at the end of the
+             * block, stop here. Those are implied anyway.
+             */
+            if (next == codeSize
+                    && nextAddrLS == Integer.MAX_VALUE
+                    && nextAddrP == Integer.MAX_VALUE) {
+                break;                
+            }
+
+            if (next == nextAddrP) {
+                // Combined advance PC + position entry
+                emitPosition(sortedPositions.get(curp++));
+            } else {
+                emitAdvancePc(next - address);
+            }
+        }
+
+        emitEndSequence();
+
+        byte[] result = output.toByteArray();
+        
+        if (DEBUG) {
+            int origSize = 0;
+            int newSize = result.length;
+
+            if (positionlist != null) {
+                origSize +=  (positionlist.size() * 6);
+            }
+
+            if (locallist != null) {
+                origSize += (4 * 5 * locallist.size());
+            }
+
+            System.err.printf(
+                    "Lines+Locals table was %d bytes is now %d bytes\n",
+                    origSize, newSize);
+        }
+        
+        return result;
+    }
+
+    /**
+     * Emits all local ends that occur at the current <code>address</code>
+     *
+     * @param curle Current index in sortedLocalsEnd
+     * @param sortedLocalsEnd Locals, sorted by ascending end address
+     * @param curls Current index in sortedLocalsStart
+     * @param sortedLocalsStart Locals, sorted by ascending start address
+     * @return new value for <code>curle</code>
+     * @throws IOException
+     */
+    private int emitLocalEndsAtAddress(int curle,
+            ArrayList<LocalList.Entry> sortedLocalsEnd, int curls,
+            ArrayList<LocalList.Entry> sortedLocalsStart)
+            throws IOException {
+
+        int szl = sortedLocalsEnd.size();
+
+        // Ignore "local ends" at end of code.
+        while (curle < szl
+                && sortedLocalsEnd.get(curle).getEnd() == address
+                && address != codeSize) {
+
+            boolean skipLocalEnd = false;
+
+            /*
+             * Check to see if there's a range-start that appears at
+             * the same address for the same register. If so, the
+             * end-range is implicit so skip it.
+             */
+            for (int j = curls; j < szl
+                    && sortedLocalsStart.get(j).getStart() == address
+                    ; j++) {
+
+                if (sortedLocalsStart.get(j).getRegister()
+                        == sortedLocalsEnd.get(curle).getRegister()) {
+                    skipLocalEnd = true;
+
+                    if (DEBUG) {
+                        System.err.printf("skip local end v%d\n",
+                                sortedLocalsEnd.get(curle).getRegister());
+                    }
+                    curle++;
+                    break;
+                }
+            }
+
+            if (!skipLocalEnd) {
+                emitLocalEnd(sortedLocalsEnd.get(curle++));
+            }
+        }
+        return curle;
+    }
+
+    /**
+     * Emits all local starts that occur at the current <code>address</code>
+     *
+     * @param curls Current index in sortedLocalsStart
+     * @param sortedLocalsStart Locals, sorted by ascending start address
+     * @return new value for <code>curls</code>
+     * @throws IOException
+     */
+    private int emitLocalStartsAtAddress(int curls,
+            ArrayList<LocalList.Entry> sortedLocalsStart)
+            throws IOException {
+
+        int szl = sortedLocalsStart.size();
+
+        while (curls < szl
+                && sortedLocalsStart.get(curls).getStart() == address) {
+            LocalList.Entry lle = sortedLocalsStart.get(curls++);
+            LocalList.Entry prevlle = lastEntryForReg[lle.getRegister()];
+
+            if (lle == prevlle) {
+                /*
+                 * Here we ignore locals entries for parameters,
+                 * which have already been represented and placed in the
+                 * lastEntryForReg array.
+                 */
+                continue;
+            } else if (prevlle != null && lle.matches(prevlle)) {
+                if (prevlle.getEnd() == lle.getStart()) {
+                    /*
+                     * An adjacent range with the same register.
+                     * The previous emitLocalEndsAtAddress() call skipped
+                     * this local end, so we'll skip this local start as well.
+                     */
+                    continue;
+                } else {
+                    emitLocalRestart(lle);
+                }
+            } else {
+                lastEntryForReg[lle.getRegister()] = lle;
+                emitLocalStart(lle);
+            }
+        }
+        return curls;
+    }
+
+    /**
+     * Emits all positions that occur at the current <code>address</code>
+     *
+     * @param curp Current index in sortedPositions
+     * @param sortedPositions positions, sorted by ascending address
+     * @return new value for <code>curp</code>
+     * @throws IOException
+     */
+    private int emitPositionsAtAddress(int curp,
+            ArrayList<PositionList.Entry> sortedPositions)
+            throws IOException {
+
+        int szp = sortedPositions.size();
+        while (curp < szp
+                && sortedPositions.get(curp).getAddress() == address) {
+            emitPosition(sortedPositions.get(curp++));
+        }
+        return curp;
+    }
+
+    /**
+     * Emits the header sequence, which consists of LEB128-encoded initial
+     * line number and string indicies for names of all non-"this" arguments.
+     *  
+     * @param sortedPositions positions, sorted by ascending address
+     * @param methodArgs local list entries for method argumens arguments,
+     * in left-to-right order omitting "this"
+     * @throws IOException
+     */
+    private void emitHeader(ArrayList<PositionList.Entry> sortedPositions,
+            ArrayList<LocalList.Entry> methodArgs) throws IOException {
+        boolean annotate = (annotateTo != null) || (debugPrint != null);
+        int mark = output.getCursor();
+
+        // Start by initializing the line number register.
+        if (sortedPositions.size() > 0) {
+            PositionList.Entry entry = sortedPositions.get(0);
+            line = entry.getPosition().getLine();
+        }
+        output.writeUnsignedLeb128(line);
+
+        if (annotate) {
+            annotate(output.getCursor() - mark, "line_start: " + line);
+        }
+
+        int curParam = getParamBase();
+        // paramTypes will not include 'this'
+        StdTypeList paramTypes = desc.getParameterTypes();
+        int szParamTypes = paramTypes.size();
+
+        /*
+         * Initialize lastEntryForReg to have an initial
+         * entry for the 'this' pointer.
+         */
+        if (!isStatic) {
+            for (LocalList.Entry arg: methodArgs) {
+                if (curParam == arg.getRegister()) {
+                    lastEntryForReg[curParam] = arg;
+                    break;
+                }
+            }
+            curParam++;
+        }
+
+        // Write out the number of parameter entries that will follow.
+        mark = output.getCursor();
+        output.writeUnsignedLeb128(szParamTypes);
+
+        if (annotate) {
+            annotate(output.getCursor() - mark, 
+                    String.format("parameters_size: %04x", szParamTypes));
+        }
+
+        /*
+         * Then emit the string indicies of all the method parameters.
+         * Note that 'this', if applicable, is excluded.
+         */
+        for (int i = 0; i < szParamTypes; i++) {
+            Type pt = paramTypes.get(i);
+            LocalList.Entry found = null;
+
+            mark = output.getCursor();
+
+            for (LocalList.Entry arg: methodArgs) {
+                if (curParam == arg.getRegister()) {
+                    found = arg;
+
+                    if (arg.getSignature() != null) {
+                        /*
+                         * Parameters with signatures will be re-emitted
+                         * in complete as LOCAL_START_EXTENDED's below.
+                         */
+                        emitStringIndex(null);
+                    } else {
+                        emitStringIndex(arg.getName());
+                    }
+                    lastEntryForReg[curParam] = arg;
+
+                    break;
+                }
+            }
+
+            if (found == null) {
+                /*
+                 * Emit a null symbol for "unnamed." This is common
+                 * for, e.g., synthesized methods and inner-class
+                 * this$0 arguments.
+                 */
+                emitStringIndex(null);
+            }
+
+            if (annotate) {
+                String parameterName
+                        = (found == null || found.getSignature() != null)
+                                ? "<unnamed>" : found.getName().toHuman();
+                annotate(output.getCursor() - mark,
+                        "parameter " + parameterName + " "
+                                + RegisterSpec.PREFIX + curParam);
+            }
+
+            curParam += pt.getCategory();
+        }
+
+        /*
+         * If anything emitted above has a type signature, emit it again as
+         * a LOCAL_RESTART_EXTENDED
+         */
+
+        for (LocalList.Entry arg: lastEntryForReg) {
+            if (arg == null) {
+                continue;
+            }
+
+            CstUtf8 signature = arg.getSignature();
+
+            if (signature != null) {
+                emitLocalStartExtended(arg);
+            }
+        }
+    }
+
+    /**
+     * Builds a list of position entries, sorted by ascending address.
+     *
+     * @return A sorted positions list
+     */
+    private ArrayList<PositionList.Entry> buildSortedPositions() {
+        int sz = (positionlist == null) ? 0 : positionlist.size();
+        ArrayList<PositionList.Entry> result = new ArrayList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.add(positionlist.get(i));
+        }
+
+        // Sort ascending by address.
+        Collections.sort (result, new Comparator<PositionList.Entry>() {
+            public int compare (PositionList.Entry a, PositionList.Entry b) {
+                return a.getAddress() - b.getAddress();
+            }
+
+            public boolean equals (Object obj) {
+               return obj == this;
+            }
+        });
+        return result;
+    }
+
+    /**
+     * Builds a list of locals entries sorted by ascending start address.
+     *
+     * @return A sorted locals list list
+     */
+    private ArrayList<LocalList.Entry> buildLocalsStart() {
+        int sz = (locallist == null) ? 0 : locallist.size();
+        ArrayList<LocalList.Entry> result = new ArrayList(sz);
+
+        // Add all the entries
+        for (int i = 0; i < sz; i++) {
+            LocalList.Entry e = locallist.get(i);
+            result.add(locallist.get(i));
+        }
+
+        // Sort ascending by start address.
+        Collections.sort (result, new Comparator<LocalList.Entry>() {
+            public int compare (LocalList.Entry a, LocalList.Entry b) {
+                return a.getStart() - b.getStart();
+            }
+
+            public boolean equals (Object obj) {
+               return obj == this;
+            }
+        });
+        return result;
+    }
+
+    /**
+     * Builds a list of locals entries sorted by ascending end address.
+     * 
+     * @param list locals list in any order
+     * @return a sorted locals list
+     */
+    private ArrayList<LocalList.Entry> buildLocalsEnd(
+            ArrayList<LocalList.Entry> list) {
+
+        ArrayList<LocalList.Entry> sortedLocalsEnd  = new ArrayList(list);
+
+        // Sort ascending by end address.
+        Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() {
+            public int compare (LocalList.Entry a, LocalList.Entry b) {
+                return a.getEnd() - b.getEnd();
+            }
+
+            public boolean equals (Object obj) {
+               return obj == this;
+            }
+        });
+        return sortedLocalsEnd;
+    }
+
+    /**
+     * Gets the register that begins the method's parameter range (including
+     * the 'this' parameter for non-static methods). The range continues until
+     * <code>regSize</code>
+     *
+     * @return register as noted above
+     */
+    private int getParamBase() {
+        return regSize
+                - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+    }
+
+    /**
+     * Extracts method arguments from a locals list. These will be collected
+     * from the input list and sorted by ascending register in the
+     * returned list.
+     *
+     * @param sortedLocals locals list, sorted by ascending start address,
+     * to process; left unmodified
+     * @return list of non-<code>this</code> method argument locals,
+     * sorted by ascending register
+     */
+    private ArrayList<LocalList.Entry> extractMethodArguments (
+            ArrayList<LocalList.Entry> sortedLocals) {
+
+        ArrayList<LocalList.Entry> result
+                = new ArrayList(desc.getParameterTypes().size());
+
+        int argBase = getParamBase();
+
+        BitSet seen = new BitSet(regSize - argBase);
+
+        int sz = sortedLocals.size();
+        for (int i = 0; i < sz; i++) {
+            LocalList.Entry e = sortedLocals.get(i);
+            int reg = e.getRegister();
+
+            if (reg < argBase) {
+                continue;
+            }
+
+            // only the lowest-start-address entry is included.
+            if (seen.get(reg - argBase)) {
+                continue;
+            }
+
+            seen.set(reg - argBase);
+            result.add(e);
+        }
+
+        // Sort by ascending register.
+        Collections.sort (result, new Comparator<LocalList.Entry>() {
+            public int compare (LocalList.Entry a, LocalList.Entry b) {
+                return a.getRegister() - b.getRegister();
+            }
+
+            public boolean equals (Object obj) {
+               return obj == this;
+            }
+        });
+
+        return result;
+    }
+
+    /**
+     * Returns a string representation of this LocalList entry that is
+     * appropriate for emitting as an annotation.
+     *
+     * @param e non-null; entry
+     * @return non-null; annotation string
+     */
+    private String entryAnnotationString(LocalList.Entry e) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(RegisterSpec.PREFIX);
+        sb.append(e.getRegister());
+        sb.append(' ');
+
+        CstUtf8 name = e.getName();
+        if (name == null) {
+            sb.append("null");
+        } else {
+            sb.append(name.toHuman());
+        }
+        sb.append(' ');
+
+        CstType type = e.getType();
+        if (type == null) {
+            sb.append("null");
+        } else {
+            sb.append(type.toHuman());
+        }
+
+        CstUtf8 signature = e.getSignature();
+
+        if (signature != null) {
+            sb.append(' ');
+            sb.append(signature.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
+     * sequence.
+     *
+     * @param entry entry associated with this restart
+     * @throws IOException
+     */
+    private void emitLocalRestart(LocalList.Entry entry)
+            throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_RESTART_LOCAL);
+        emitUnsignedLeb128(entry.getRegister());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +local restart %s",
+                            address, entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local restart");
+        }
+    }
+
+    /**
+     * Emits a string index as an unsigned LEB128. The actual value written
+     * is shifted by 1, so that the '0' value is reserved for "null". The
+     * null symbol is used in some cases by the parameter name list
+     * at the beginning of the sequence.
+     *
+     * @param string null-ok; string to emit
+     * @throws IOException
+     */
+    private void emitStringIndex(CstUtf8 string) throws IOException {
+        if ((string == null) || (file == null)) {
+            output.writeUnsignedLeb128(0);
+        } else {
+            output.writeUnsignedLeb128(
+                1 + file.getStringIds().indexOf(string));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emit string %s\n",
+                    string == null ? "<null>" : string.toQuoted());
+        }
+    }
+
+    /**
+     * Emits a type index as an unsigned LEB128. The actual value written
+     * is shifted by 1, so that the '0' value is reserved for "null".
+     *
+     * @param type null-ok; type to emit
+     * @throws IOException
+     */
+    private void emitTypeIndex(CstType type) throws IOException {
+        if ((type == null) || (file == null)) {
+            output.writeUnsignedLeb128(0);
+        } else {
+            output.writeUnsignedLeb128(
+                1 + file.getTypeIds().indexOf(type));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emit type %s\n",
+                    type == null ? "<null>" : type.toHuman());
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
+     * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+     * DBG_START_LOCAL_EXTENDED} sequence.
+     *
+     * @param entry entry to emit
+     * @throws IOException
+     */
+    private void emitLocalStart(LocalList.Entry entry)
+        throws IOException {
+
+        if (entry.getSignature() != null) {
+            emitLocalStartExtended(entry);
+            return;
+        }
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_START_LOCAL);
+
+        emitUnsignedLeb128 (entry.getRegister());
+        emitStringIndex(entry.getName());
+        emitTypeIndex(entry.getType());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +local %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local start");
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+     * DBG_START_LOCAL_EXTENDED} sequence.
+     *
+     * @param entry entry to emit
+     * @throws IOException
+     */
+    private void emitLocalStartExtended(LocalList.Entry entry)
+        throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_START_LOCAL_EXTENDED);
+
+        emitUnsignedLeb128 (entry.getRegister());
+        emitStringIndex(entry.getName());
+        emitTypeIndex(entry.getType());
+        emitStringIndex(entry.getSignature());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +localx %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local start");
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
+     *
+     * @param entry entry non-null; entry associated with end.
+     * @throws IOException
+     */
+    private void emitLocalEnd(LocalList.Entry entry)
+            throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_END_LOCAL);
+        output.writeUnsignedLeb128(entry.getRegister());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: -local %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local end");
+        }
+    }
+
+    /**
+     * Emits the necessary byte sequences to emit the given position table
+     * entry. This will typically be a single special opcode, although
+     * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
+     *
+     * @param entry position entry to emit.
+     * @throws IOException
+     */
+    private void emitPosition(PositionList.Entry entry)
+            throws IOException {
+
+        SourcePosition pos = entry.getPosition();
+        int newLine = pos.getLine();
+        int newAddress = entry.getAddress();
+
+        int opcode;
+
+        int deltaLines = newLine - line;
+        int deltaAddress = newAddress - address;
+
+        if (deltaAddress < 0) {
+            throw new RuntimeException(
+                    "Position entries must be in ascending address order");
+        }
+
+        if ((deltaLines < DBG_LINE_BASE)
+                || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
+            emitAdvanceLine(deltaLines);
+            deltaLines = 0;
+        }
+
+        opcode = computeOpcode (deltaLines, deltaAddress);
+
+        if ((opcode & ~0xff) > 0) {
+            emitAdvancePc(deltaAddress);
+            deltaAddress = 0;
+            opcode = computeOpcode (deltaLines, deltaAddress);
+
+            if ((opcode & ~0xff) > 0) {
+                emitAdvanceLine(deltaLines);
+                deltaLines = 0;
+                opcode = computeOpcode (deltaLines, deltaAddress);
+            }
+        }
+
+        output.writeByte(opcode);
+
+        line += deltaLines;
+        address += deltaAddress;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1,
+                    String.format ("%04x: line %d", address, line));
+        }
+    }
+
+    /**
+     * Computes a special opcode that will encode the given position change.
+     * If the return value is > 0xff, then the request cannot be fulfilled.
+     * Essentially the same as described in "DWARF Debugging Format Version 3"
+     * section 6.2.5.1.
+     *
+     * @param deltaLines &gt;= DBG_LINE_BASE and &lt;= DBG_LINE_BASE +
+     * DBG_LINE_RANGE, the line change to encode
+     * @param deltaAddress &gt;= 0; the address change to encode
+     * @return &lt;= 0xff if in range, otherwise parameters are out of range
+     */
+    private static int computeOpcode(int deltaLines, int deltaAddress) {
+        if (deltaLines < DBG_LINE_BASE
+                || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
+
+            throw new RuntimeException ("Parameter out of range");            
+        }
+
+        return  (deltaLines - DBG_LINE_BASE)
+            + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
+    }
+
+    /**
+     * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
+     * sequence.
+     *
+     * @param deltaLines amount to change line number register by
+     * @throws IOException
+     */
+    private void emitAdvanceLine(int deltaLines) throws IOException {
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_ADVANCE_LINE);
+        output.writeSignedLeb128(deltaLines);
+        line += deltaLines;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("line = %d", line));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emitting advance_line for %d\n", deltaLines);
+        }
+    }
+
+    /**
+     * Emits an  {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} 
+     * sequence.
+     *
+     * @param deltaAddress &gt;= 0 amount to change program counter by
+     * @throws IOException
+     */
+    private void emitAdvancePc(int deltaAddress) throws IOException {
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_ADVANCE_PC);
+        output.writeUnsignedLeb128(deltaAddress);
+        address += deltaAddress;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: advance pc", address));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
+        }
+    }
+
+    /**
+     * Emits an unsigned LEB128 value.
+     *
+     * @param n &gt= 0 vallue to emit. Note that, although this can represent
+     * integers larger than Integer.MAX_VALUE, we currently don't allow that.
+     * @throws IOException
+     */
+    private void emitUnsignedLeb128(int n) throws IOException {
+        // We'll never need the top end of the unsigned range anyway.
+        if (n < 0) {
+            throw new RuntimeException(
+                    "Signed value where unsigned required: " + n);
+        }
+
+        output.writeSignedLeb128(n);
+    }
+
+    /**
+     * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
+     * bytecode.
+     */
+    private void emitEndSequence() {
+        output.writeByte(DBG_END_SEQUENCE);
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1, "end sequence");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
new file mode 100644
index 0000000..b074593
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.PrintWriter;
+
+public class DebugInfoItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    private static final boolean ENABLE_ENCODER_SELF_CHECK = false;
+
+    /** non-null; the code this item represents */
+    private final DalvCode code;
+    
+    private byte[] encoded;
+
+    private final boolean isStatic;
+    private final CstMethodRef ref;
+
+    public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) {
+        // We don't know the write size yet.
+        super (ALIGNMENT, -1);
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        this.code = code;
+        this.isStatic = isStatic;
+        this.ref = ref;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_DEBUG_INFO_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // No contents to add.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        try {
+            encoded = encode(addedTo.getFile(), null, null, null, false);
+            setWriteSize(encoded.length);
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while placing debug info for " + ref.toHuman());
+        }
+    }
+        
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /**
+     * Writes annotations for the elements of this list, as
+     * zero-length. This is meant to be used for dumping this instance
+     * directly after a code dump (with the real local list actually
+     * existing elsewhere in the output).
+     *
+     * @param file non-null; the file to use for referencing other sections
+     * @param out non-null; where to annotate to
+     * @param prefix null-ok; prefix to attach to each line of output
+     */
+    public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) {
+        encode(file, prefix, null, out, false);
+    }    
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out non-null; where to dump
+     * @param prefix non-null; prefix to attach to each line of output
+     */
+    public void debugPrint(PrintWriter out, String prefix) {
+        encode(null, prefix, out, null, false);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        if (out.annotates()) {
+            /*
+             * Re-run the encoder to generate the annotations,
+             * but write the bits from the original encode
+             */
+
+            out.annotate(offsetString() + " debug info");
+            encode(file, null, null, out, true);
+        }
+
+        out.write(encoded);
+    }
+
+    /**
+     * Performs debug info encoding.
+     *
+     * @param file null-ok; file to refer to during encoding
+     * @param prefix null-ok; prefix to attach to each line of output
+     * @param debugPrint null-ok; if specified, an alternate output for
+     * annotations
+     * @param out null-ok; if specified, where annotations should go
+     * @param consume whether to claim to have consumed output for
+     * <code>out</code>
+     * @return non-null; the encoded array
+     */
+    private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
+            AnnotatedOutput out, boolean consume) {
+        PositionList positions = code.getPositions();
+        LocalList locals = code.getLocals();
+        DalvInsnList insns = code.getInsns();
+        int codeSize = insns.codeSize();
+        int regSize = insns.getRegistersSize();
+
+        DebugInfoEncoder encoder =
+            new DebugInfoEncoder(positions, locals,
+                    file, codeSize, regSize, isStatic, ref);
+
+        byte[] result;
+
+        if ((debugPrint == null) && (out == null)) {
+            result = encoder.convert();
+        } else {
+            result = encoder.convertAndAnnotate(
+                    prefix, debugPrint, out, consume);
+        }
+
+        if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
+            DebugInfoDecoder.validateEncode(encoded, codeSize,
+                    regSize, positions, locals, isStatic, ref, file);
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java
new file mode 100644
index 0000000..8a4075d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+import static com.android.dx.dex.file.MixedItemSection.SortType;
+
+/**
+ * Representation of an entire <code>.dex</code> (Dalvik EXecutable)
+ * file, which itself consists of a set of Dalvik classes.
+ */
+public final class DexFile {
+    /** non-null; word data section */
+    private final MixedItemSection wordData;
+
+    /** 
+     * non-null; type lists section. This is word data, but separating
+     * it from {@link #wordData} helps break what would otherwise be a
+     * circular dependency between the that and {@link #protoIds}.
+     */
+    private final MixedItemSection typeLists;
+
+    /**
+     * non-null; map section. The map needs to be in a section by itself
+     * for the self-reference mechanics to work in a reasonably
+     * straightforward way. See {@link MapItem#addMap} for more detail.
+     */
+    private final MixedItemSection map;
+
+    /** non-null; string data section */
+    private final MixedItemSection stringData;
+
+    /** non-null; string identifiers section */
+    private final StringIdsSection stringIds;
+
+    /** non-null; type identifiers section */
+    private final TypeIdsSection typeIds;
+
+    /** non-null; prototype identifiers section */
+    private final ProtoIdsSection protoIds;
+
+    /** non-null; field identifiers section */
+    private final FieldIdsSection fieldIds;
+
+    /** non-null; method identifiers section */
+    private final MethodIdsSection methodIds;
+
+    /** non-null; class definitions section */
+    private final ClassDefsSection classDefs;
+
+    /** non-null; class data section */
+    private final MixedItemSection classData;
+
+    /** non-null; byte data section */
+    private final MixedItemSection byteData;
+
+    /** non-null; file header */
+    private final HeaderSection header;
+
+    /**
+     * non-null; array of sections in the order they will appear in the
+     * final output file
+     */
+    private final Section[] sections;
+
+    /** &gt;= -1; total file size or <code>-1</code> if unknown */
+    private int fileSize;
+
+    /** &gt;= 40; maximum width of the file dump */
+    private int dumpWidth;
+
+    /**
+     * Constructs an instance. It is initially empty.
+     */
+    public DexFile() {
+        header = new HeaderSection(this);
+        typeLists = new MixedItemSection(null, this, 4, SortType.NONE);
+        wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE);
+        stringData =
+            new MixedItemSection("string_data", this, 1, SortType.INSTANCE);
+        classData = new MixedItemSection(null, this, 1, SortType.NONE);
+        byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE);
+        stringIds = new StringIdsSection(this);
+        typeIds = new TypeIdsSection(this);
+        protoIds = new ProtoIdsSection(this);
+        fieldIds = new FieldIdsSection(this);
+        methodIds = new MethodIdsSection(this);
+        classDefs = new ClassDefsSection(this);
+        map = new MixedItemSection("map", this, 4, SortType.NONE);
+
+        /* 
+         * This is the list of sections in the order they appear in
+         * the final output.
+         */
+        sections = new Section[] {
+            header, stringIds, typeIds, protoIds, fieldIds, methodIds,
+            classDefs, wordData, typeLists, stringData, byteData, 
+            classData, map };
+        
+        fileSize = -1;
+        dumpWidth = 79;
+    }
+
+    /**
+     * Adds a class to this instance. It is illegal to attempt to add more
+     * than one class with the same name.
+     * 
+     * @param clazz non-null; the class to add
+     */
+    public void add(ClassDefItem clazz) {
+        classDefs.add(clazz);
+    }
+
+    /**
+     * Gets the class definition with the given name, if any.
+     * 
+     * @param name non-null; the class name to look for
+     * @return null-ok; the class with the given name, or <code>null</code>
+     * if there is no such class
+     */
+    public ClassDefItem getClassOrNull(String name) {
+        try {
+            Type type = Type.internClassName(name);
+            return (ClassDefItem) classDefs.get(new CstType(type));
+        } catch (IllegalArgumentException ex) {
+            // Translate exception, per contract.
+            return null;
+        }
+    }
+
+    /**
+     * Writes the contents of this instance as either a binary or a
+     * human-readable form, or both.
+     * 
+     * @param out null-ok; where to write to
+     * @param humanOut null-ok; where to write human-oriented output to
+     * @param verbose whether to be verbose when writing human-oriented output
+     */
+    public void writeTo(OutputStream out, Writer humanOut, boolean verbose)
+        throws IOException {
+        boolean annotate = (humanOut != null);
+        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+        if (out != null) {
+            out.write(result.getArray());
+        }
+
+        if (annotate) {
+            result.writeAnnotationsTo(humanOut);
+        }
+    }
+
+    /**
+     * Returns the contents of this instance as a <code>.dex</code> file,
+     * in <code>byte[]</code> form.
+     * 
+     * @param humanOut null-ok; where to write human-oriented output to
+     * @param verbose whether to be verbose when writing human-oriented output
+     * @return non-null; a <code>.dex</code> file for this instance
+     */
+    public byte[] toDex(Writer humanOut, boolean verbose) 
+        throws IOException {
+        boolean annotate = (humanOut != null);
+        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+        if (annotate) {
+            result.writeAnnotationsTo(humanOut);
+        }
+
+        return result.getArray();
+    }
+
+    /**
+     * Sets the maximum width of the human-oriented dump of the instance.
+     * 
+     * @param dumpWidth &gt;= 40; the width
+     */
+    public void setDumpWidth(int dumpWidth) {
+        if (dumpWidth < 40) {
+            throw new IllegalArgumentException("dumpWidth < 40");
+        }
+
+        this.dumpWidth = dumpWidth;
+    }
+
+    /**
+     * Gets the total file size, if known.
+     * 
+     * <p>This is package-scope in order to allow
+     * the {@link HeaderSection} to set itself up properly.</p>
+     * 
+     * @return &gt;= 0; the total file size
+     * @throws RuntimeException thrown if the file size is not yet known
+     */
+    /*package*/ int getFileSize() {
+        if (fileSize < 0) {
+            throw new RuntimeException("file size not yet known");
+        }
+
+        return fileSize;
+    }
+
+    /**
+     * Gets the string data section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the string data section
+     */
+    /*package*/ MixedItemSection getStringData() {
+        return stringData;
+    }
+
+    /**
+     * Gets the word data section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the word data section
+     */
+    /*package*/ MixedItemSection getWordData() {
+        return wordData;
+    }
+
+    /**
+     * Gets the type lists section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the word data section
+     */
+    /*package*/ MixedItemSection getTypeLists() {
+        return typeLists;
+    }
+
+    /**
+     * Gets the map section.
+     * 
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     * 
+     * @return non-null; the map section
+     */
+    /*package*/ MixedItemSection getMap() {
+        return map;
+    }
+
+    /**
+     * Gets the string identifiers section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the string identifiers section
+     */
+    /*package*/ StringIdsSection getStringIds() {
+        return stringIds;
+    }
+
+    /**
+     * Gets the class definitions section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the class definitions section
+     */
+    /*package*/ ClassDefsSection getClassDefs() {
+        return classDefs;
+    }
+
+    /**
+     * Gets the class data section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the class data section
+     */
+    /*package*/ MixedItemSection getClassData() {
+        return classData;
+    }
+
+    /**
+     * Gets the type identifiers section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the class identifiers section
+     */
+    /*package*/ TypeIdsSection getTypeIds() {
+        return typeIds;
+    }
+
+    /**
+     * Gets the prototype identifiers section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the prototype identifiers section
+     */
+    /*package*/ ProtoIdsSection getProtoIds() {
+        return protoIds;
+    }
+
+    /**
+     * Gets the field identifiers section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the field identifiers section
+     */
+    /*package*/ FieldIdsSection getFieldIds() {
+        return fieldIds;
+    }
+
+    /**
+     * Gets the method identifiers section.
+     * 
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     * 
+     * @return non-null; the method identifiers section
+     */
+    /*package*/ MethodIdsSection getMethodIds() {
+        return methodIds;
+    }
+
+    /**
+     * Gets the byte data section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return non-null; the byte data section
+     */
+    /*package*/ MixedItemSection getByteData() {
+        return byteData;
+    }
+
+    /**
+     * Gets the first section of the file that is to be considered
+     * part of the data section.
+     * 
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     * 
+     * @return non-null; the section
+     */
+    /*package*/ Section getFirstDataSection() {
+        return wordData;
+    }
+
+    /**
+     * Gets the last section of the file that is to be considered
+     * part of the data section.
+     * 
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     * 
+     * @return non-null; the section
+     */
+    /*package*/ Section getLastDataSection() {
+        return map;
+    }
+    
+    /**
+     * Interns the given constant in the appropriate section of this
+     * instance, or do nothing if the given constant isn't the sort
+     * that should be interned.
+     * 
+     * @param cst non-null; constant to possibly intern
+     */
+    /*package*/ void internIfAppropriate(Constant cst) {
+        if (cst instanceof CstString) {
+            stringIds.intern((CstString) cst);
+        } else if (cst instanceof CstUtf8) {
+            stringIds.intern((CstUtf8) cst);
+        } else if (cst instanceof CstType) {
+            typeIds.intern((CstType) cst);
+        } else if (cst instanceof CstBaseMethodRef) {
+            methodIds.intern((CstBaseMethodRef) cst);
+        } else if (cst instanceof CstFieldRef) {
+            fieldIds.intern((CstFieldRef) cst);
+        } else if (cst instanceof CstEnumRef) {
+            fieldIds.intern(((CstEnumRef) cst).getFieldRef());
+        } else if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+    }
+
+    /**
+     * Gets the {@link IndexedItem} corresponding to the given constant,
+     * if it is a constant that has such a correspondence, or return
+     * <code>null</code> if it isn't such a constant. This will throw
+     * an exception if the given constant <i>should</i> have been found
+     * but wasn't.
+     * 
+     * @param cst non-null; the constant to look up
+     * @return null-ok; its corresponding item, if it has a corresponding
+     * item, or <code>null</code> if it's not that sort of constant
+     */
+    /*package*/ IndexedItem findItemOrNull(Constant cst) {
+        IndexedItem item;
+
+        if (cst instanceof CstString) {
+            return stringIds.get(cst);
+        } else if (cst instanceof CstType) {
+            return typeIds.get(cst);
+        } else if (cst instanceof CstBaseMethodRef) {
+            return methodIds.get(cst);
+        } else if (cst instanceof CstFieldRef) {
+            return fieldIds.get(cst);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the contents of this instance as a <code>.dex</code> file,
+     * in a {@link ByteArrayAnnotatedOutput} instance.
+     * 
+     * @param annotate whether or not to keep annotations
+     * @param verbose if annotating, whether to be verbose
+     * @return non-null; a <code>.dex</code> file for this instance
+     */
+    private ByteArrayAnnotatedOutput toDex0(boolean annotate,
+            boolean verbose) {
+        /*
+         * The following is ordered so that the prepare() calls which
+         * add items happen before the calls to the sections that get
+         * added to.
+         */
+
+        classDefs.prepare();
+        classData.prepare();
+        wordData.prepare();
+        byteData.prepare();
+        methodIds.prepare();
+        fieldIds.prepare();
+        protoIds.prepare();
+        typeLists.prepare();
+        typeIds.prepare();
+        stringIds.prepare();
+        stringData.prepare();
+        header.prepare();
+
+        // Place the sections within the file.
+
+        int count = sections.length;
+        int offset = 0;
+
+        for (int i = 0; i < count; i++) {
+            Section one = sections[i];
+            int placedAt = one.setFileOffset(offset);
+            if (placedAt < offset) {
+                throw new RuntimeException("bogus placement for section " + i);
+            }
+
+            try {
+                if (one == map) {
+                    /*
+                     * Inform the map of all the sections, and add it
+                     * to the file. This can only be done after all
+                     * the other items have been sorted and placed.
+                     */
+                    MapItem.addMap(sections, map);
+                    map.prepare();
+                }
+
+                if (one instanceof MixedItemSection) {
+                    /*
+                     * Place the items of a MixedItemSection that just
+                     * got placed.
+                     */
+                    ((MixedItemSection) one).placeItems();
+                }
+            
+                offset = placedAt + one.writeSize();
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while writing section " + i);
+            }
+        }
+
+        // Write out all the sections.
+
+        fileSize = offset;
+        byte[] barr = new byte[fileSize];
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr);
+
+        if (annotate) {
+            out.enableAnnotations(dumpWidth, verbose);
+        }
+
+        for (int i = 0; i < count; i++) {
+            try {
+                Section one = sections[i];
+                int zeroCount = one.getFileOffset() - out.getCursor();
+                if (zeroCount < 0) {
+                    throw new ExceptionWithContext("excess write of " +
+                            (-zeroCount));
+                }
+                out.writeZeroes(one.getFileOffset() - out.getCursor());
+                one.writeTo(out);
+            } catch (RuntimeException ex) {
+                ExceptionWithContext ec;
+                if (ex instanceof ExceptionWithContext) {
+                    ec = (ExceptionWithContext) ex;
+                } else {
+                    ec = new ExceptionWithContext(ex);
+                }
+                ec.addContext("...while writing section " + i);
+                throw ec;
+            }
+        }
+
+        if (out.getCursor() != fileSize) {
+            throw new RuntimeException("foreshortened write");
+        }
+
+        // Perform final bookkeeping.
+        
+        calcSignature(barr);
+        calcChecksum(barr);
+
+        if (annotate) {
+            wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
+                    "\nmethod code index:\n\n");
+            getStatistics().writeAnnotation(out);
+            out.finishAnnotating();
+        }
+
+        return out;
+    }
+
+    /**
+     * Generates and returns statistics for all the items in the file.
+     * 
+     * @return non-null; the statistics
+     */
+    public Statistics getStatistics() {
+        Statistics stats = new Statistics();
+
+        for (Section s : sections) {
+            stats.addAll(s);
+        }
+
+        return stats;
+    }
+
+    /**
+     * Calculates the signature for the <code>.dex</code> file in the
+     * given array, and modify the array to contain it.
+     * 
+     * @param bytes non-null; the bytes of the file
+     */
+    private static void calcSignature(byte[] bytes) {
+        MessageDigest md;
+
+        try {
+            md = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        md.update(bytes, 32, bytes.length - 32);
+
+        try {
+            int amt = md.digest(bytes, 12, 20);
+            if (amt != 20) {
+                throw new RuntimeException("unexpected digest write: " + amt +
+                                           " bytes");
+            }
+        } catch (DigestException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Calculates the checksum for the <code>.dex</code> file in the
+     * given array, and modify the array to contain it.
+     * 
+     * @param bytes non-null; the bytes of the file
+     */
+    private static void calcChecksum(byte[] bytes) {
+        Adler32 a32 = new Adler32();
+
+        a32.update(bytes, 12, bytes.length - 12);
+
+        int sum = (int) a32.getValue();
+
+        bytes[8]  = (byte) sum;
+        bytes[9]  = (byte) (sum >> 8);
+        bytes[10] = (byte) (sum >> 16);
+        bytes[11] = (byte) (sum >> 24);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedArrayItem.java b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
new file mode 100644
index 0000000..9ec72fa
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Encoded array of constant values.
+ */
+public final class EncodedArrayItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    /** non-null; the array to represent */
+    private final CstArray array;
+
+    /**
+     * null-ok; encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param array non-null; array to represent
+     */
+    public EncodedArrayItem(CstArray array) {
+        /*
+         * The write size isn't known up-front because (the variable-lengthed)
+         * leb128 type is used to represent some things.
+         */
+        super(ALIGNMENT, -1);
+
+        if (array == null) {
+            throw new NullPointerException("array == null");
+        }
+
+        this.array = array;
+        this.encodedForm = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ENCODED_ARRAY_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return array.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        EncodedArrayItem otherArray = (EncodedArrayItem) other;
+
+        return array.compareTo(otherArray.array);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return array.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        ValueEncoder.addContents(file, array);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+        encoder.writeArray(array, false);
+        encodedForm = out.toByteArray();
+        setWriteSize(encodedForm.length);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " encoded array");
+
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            ValueEncoder encoder = new ValueEncoder(file, out);
+            encoder.writeArray(array, true);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedField.java b/dx/src/com/android/dx/dex/file/EncodedField.java
new file mode 100644
index 0000000..62a5789
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedField.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a field of a class, of any sort.
+ */
+public final class EncodedField extends EncodedMember
+        implements Comparable<EncodedField> {
+    /** non-null; constant for the field */
+    private final CstFieldRef field;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param field non-null; constant for the field
+     * @param accessFlags access flags
+     */
+    public EncodedField(CstFieldRef field, int accessFlags) {
+        super(accessFlags);
+
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        /*
+         * TODO: Maybe check accessFlags, at least for
+         * easily-checked stuff?
+         */
+
+        this.field = field;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return field.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof EncodedField)) {
+            return false;
+        }
+
+        return compareTo((EncodedField) other) == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * <p><b>Note:</b> This compares the method constants only,
+     * ignoring any associated code, because it should never be the
+     * case that two different items with the same method constant
+     * ever appear in the same list (or same file, even).</p>
+     */
+    public int compareTo(EncodedField other) {
+        return field.compareTo(other.field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(Hex.u2(getAccessFlags()));
+        sb.append(' ');
+        sb.append(field);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        FieldIdsSection fieldIds = file.getFieldIds();
+        fieldIds.intern(field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public CstUtf8 getName() {
+        return field.getNat().getName();
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return field.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void debugPrint(PrintWriter out, boolean verbose) {
+        // TODO: Maybe put something better here?
+        out.println(toString());
+    }
+
+    /**
+     * Gets the constant for the field.
+     * 
+     * @return non-null; the constant
+     */
+    public CstFieldRef getRef() {
+        return field;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int encode(DexFile file, AnnotatedOutput out, 
+            int lastIndex, int dumpSeq) {
+        int fieldIdx = file.getFieldIds().indexOf(field);
+        int diff = fieldIdx - lastIndex;
+        int accessFlags = getAccessFlags();
+
+        if (out.annotates()) {
+            out.annotate(0, String.format("  [%x] %s", dumpSeq,
+                            field.toHuman()));
+            out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+                    "    field_idx:    " + Hex.u4(fieldIdx));
+            out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+                    "    access_flags: " +
+                    AccessFlags.fieldString(accessFlags));
+        }
+
+        out.writeUnsignedLeb128(diff);
+        out.writeUnsignedLeb128(accessFlags);
+
+        return fieldIdx;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMember.java b/dx/src/com/android/dx/dex/file/EncodedMember.java
new file mode 100644
index 0000000..3ae0d09
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMember.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ToHuman;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a member (field or method) of a class, for the
+ * purposes of encoding it inside a {@link ClassDataItem}.
+ */
+public abstract class EncodedMember implements ToHuman {
+    /** access flags */
+    private final int accessFlags;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param accessFlags access flags for the member
+     */
+    public EncodedMember(int accessFlags) {
+        this.accessFlags = accessFlags;
+    }
+
+    /**
+     * Gets the access flags.
+     *
+     * @return the access flags
+     */
+    public final int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /**
+     * Gets the name.
+     *
+     * @return non-null; the name
+     */
+    public abstract CstUtf8 getName();
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out non-null; where to dump
+     * @param verbose whether to be verbose with the output
+     */
+    public abstract void debugPrint(PrintWriter out, boolean verbose);
+
+    /**
+     * Populates a {@link DexFile} with items from within this instance.
+     * 
+     * @param file non-null; the file to populate
+     */
+    public abstract void addContents(DexFile file);
+
+    /**
+     * Encodes this instance to the given output.
+     * 
+     * @param file non-null; file this instance is part of
+     * @param out non-null; where to write to
+     * @param lastIndex &gt;= 0; the previous member index value encoded, or
+     * <code>0</code> if this is the first element to encode
+     * @param dumpSeq &gt;= 0; sequence number of this instance for
+     * annotation purposes
+     * @return &gt;= 0; the member index value that was encoded
+     */
+    public abstract int encode(DexFile file, AnnotatedOutput out, 
+            int lastIndex, int dumpSeq);
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMethod.java b/dx/src/com/android/dx/dex/file/EncodedMethod.java
new file mode 100644
index 0000000..319fbb7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMethod.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class that representats a method of a class.
+ */
+public final class EncodedMethod extends EncodedMember 
+        implements Comparable<EncodedMethod> {
+    /** non-null; constant for the method */
+    private final CstMethodRef method;
+
+    /**
+     * null-ok; code for the method, if the method is neither
+     * <code>abstract</code> nor <code>native</code> 
+     */
+    private final CodeItem code;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; constant for the method
+     * @param accessFlags access flags
+     * @param code null-ok; code for the method, if it is neither
+     * <code>abstract</code> nor <code>native</code>
+     * @param throwsList non-null; list of possibly-thrown exceptions,
+     * just used in generating debugging output (listings)
+     */
+    public EncodedMethod(CstMethodRef method, int accessFlags,
+            DalvCode code, TypeList throwsList) {
+        super(accessFlags);
+
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.method = method;
+
+        if (code == null) {
+            this.code = null;
+        } else {
+            boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
+            this.code = new CodeItem(method, code, isStatic, throwsList);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof EncodedMethod)) {
+            return false;
+        }
+
+        return compareTo((EncodedMethod) other) == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * <p><b>Note:</b> This compares the method constants only,
+     * ignoring any associated code, because it should never be the
+     * case that two different items with the same method constant
+     * ever appear in the same list (or same file, even).</p>
+     */
+    public int compareTo(EncodedMethod other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(Hex.u2(getAccessFlags()));
+        sb.append(' ');
+        sb.append(method);
+
+        if (code != null) {
+            sb.append(' ');
+            sb.append(code);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+
+        if (code != null) {
+            wordData.add(code);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final String toHuman() {
+        return method.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final CstUtf8 getName() {
+        return method.getNat().getName();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void debugPrint(PrintWriter out, boolean verbose) {
+        if (code == null) {
+            out.println(getRef().toHuman() + ": abstract or native");
+        } else {
+            code.debugPrint(out, "  ", verbose);
+        }
+    }
+
+    /**
+     * Gets the constant for the method.
+     * 
+     * @return non-null; the constant
+     */
+    public final CstMethodRef getRef() {
+        return method;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int encode(DexFile file, AnnotatedOutput out, 
+            int lastIndex, int dumpSeq) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int diff = methodIdx - lastIndex;
+        int accessFlags = getAccessFlags();
+        int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
+        boolean hasCode = (codeOff != 0);
+        boolean shouldHaveCode = (accessFlags & 
+                (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
+
+        /*
+         * Verify that code appears if and only if a method is
+         * declared to have it.
+         */
+        if (hasCode != shouldHaveCode) {
+            throw new UnsupportedOperationException(
+                    "code vs. access_flags mismatch");
+        }
+
+        if (out.annotates()) {
+            out.annotate(0, String.format("  [%x] %s", dumpSeq,
+                            method.toHuman()));
+            out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+                    "    method_idx:   " + Hex.u4(methodIdx));
+            out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+                    "    access_flags: " +
+                    AccessFlags.methodString(accessFlags));
+            out.annotate(Leb128Utils.unsignedLeb128Size(codeOff),
+                    "    code_off:     " + Hex.u4(codeOff));
+        }
+
+        out.writeUnsignedLeb128(diff);
+        out.writeUnsignedLeb128(accessFlags);
+        out.writeUnsignedLeb128(codeOff);
+
+        return methodIdx;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
new file mode 100644
index 0000000..e6169bd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a field and its annotations.
+ */
+public final class FieldAnnotationStruct
+        implements ToHuman, Comparable<FieldAnnotationStruct> {
+    /** non-null; the field in question */
+    private final CstFieldRef field;
+
+    /** non-null; the associated annotations */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param field non-null; the field in question
+     * @param annotations non-null; the associated annotations
+     */
+    public FieldAnnotationStruct(CstFieldRef field,
+            AnnotationSetItem annotations) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.field = field;
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return field.hashCode();
+    }
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof FieldAnnotationStruct)) {
+            return false;
+        }
+        
+        return field.equals(((FieldAnnotationStruct) other).field);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(FieldAnnotationStruct other) {
+        return field.compareTo(other.field);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        FieldIdsSection fieldIds = file.getFieldIds();
+        MixedItemSection wordData = file.getWordData();
+
+        fieldIds.intern(field);
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int fieldIdx = file.getFieldIds().indexOf(field);
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + field.toHuman());
+            out.annotate(4, "      field_idx:       " + Hex.u4(fieldIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(fieldIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return field.toHuman() + ": " + annotations;
+    }
+
+    /**
+     * Gets the field this item is for.
+     * 
+     * @return non-null; the field
+     */
+    public CstFieldRef getField() {
+        return field;
+    }
+
+    /**
+     * Gets the associated annotations.
+     * 
+     * @return non-null; the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations.getAnnotations();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdItem.java b/dx/src/com/android/dx/dex/file/FieldIdItem.java
new file mode 100644
index 0000000..d098d52
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstFieldRef;
+
+/**
+ * Representation of a field reference inside a Dalvik file.
+ */
+public final class FieldIdItem extends MemberIdItem {
+    /**
+     * Constructs an instance.
+     * 
+     * @param field non-null; the constant for the field
+     */
+    public FieldIdItem(CstFieldRef field) {
+        super(field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_FIELD_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        TypeIdsSection typeIds = file.getTypeIds();
+        typeIds.intern(getFieldRef().getType());
+    }
+
+    /**
+     * Gets the field constant.
+     * 
+     * @return non-null; the constant
+     */
+    public CstFieldRef getFieldRef() {
+        return (CstFieldRef) getRef();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getTypoidIdx(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        return typeIds.indexOf(getFieldRef().getType());
+    }
+        
+    /** {@inheritDoc} */
+    @Override
+    protected String getTypoidName() {
+        return "type_idx";
+    }    
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdsSection.java b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
new file mode 100644
index 0000000..fddf55f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Field refs list section of a <code>.dex</code> file.
+ */
+public final class FieldIdsSection extends MemberIdsSection {
+    /**
+     * non-null; map from field constants to {@link
+     * FieldIdItem} instances 
+     */
+    private final TreeMap<CstFieldRef, FieldIdItem> fieldIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public FieldIdsSection(DexFile file) {
+        super("field_ids", file);
+
+        fieldIds = new TreeMap<CstFieldRef, FieldIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return fieldIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        IndexedItem result = fieldIds.get((CstFieldRef) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = fieldIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "field_ids_size:  " + Hex.u4(sz));
+            out.annotate(4, "field_ids_off:   " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param field non-null; the reference to intern
+     * @return non-null; the interned reference
+     */
+    public FieldIdItem intern(CstFieldRef field) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        throwIfPrepared();
+
+        FieldIdItem result = fieldIds.get(field);
+
+        if (result == null) {
+            result = new FieldIdItem(field);
+            fieldIds.put(field, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given reference, which must have been added
+     * to this instance.
+     * 
+     * @param ref non-null; the reference to look up
+     * @return &gt;= 0; the reference's index
+     */
+    public int indexOf(CstFieldRef ref) {
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        throwIfNotPrepared();
+
+        FieldIdItem item = fieldIds.get(ref);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderItem.java b/dx/src/com/android/dx/dex/file/HeaderItem.java
new file mode 100644
index 0000000..55c1f1c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderItem.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * File header section of a <code>.dex</code> file.
+ */
+public final class HeaderItem extends IndexedItem {
+    /**
+     * non-null; the file format magic number, represented as the
+     * low-order bytes of a string
+     */
+    private static final String MAGIC = "dex\n035\0";
+
+    /** size of this section, in bytes */
+    private static final int HEADER_SIZE = 0x70;
+
+    /** the endianness tag */
+    private static final int ENDIAN_TAG = 0x12345678;
+
+    /**
+     * Constructs an instance.
+     */
+    public HeaderItem() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_HEADER_ITEM;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return HEADER_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // Nothing to do here.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int mapOff = file.getMap().getFileOffset();
+        Section firstDataSection = file.getFirstDataSection();
+        Section lastDataSection = file.getLastDataSection();
+        int dataOff = firstDataSection.getFileOffset();
+        int dataSize = lastDataSection.getFileOffset() +
+            lastDataSection.writeSize() - dataOff;
+        
+        if (out.annotates()) {
+            out.annotate(8, "magic: " + new CstUtf8(MAGIC).toQuoted());
+            out.annotate(4, "checksum");
+            out.annotate(20, "signature");
+            out.annotate(4, "file_size:       " +
+                         Hex.u4(file.getFileSize()));
+            out.annotate(4, "header_size:     " + Hex.u4(HEADER_SIZE));
+            out.annotate(4, "endian_tag:      " + Hex.u4(ENDIAN_TAG));
+            out.annotate(4, "link_size:       0");
+            out.annotate(4, "link_off:        0");
+            out.annotate(4, "map_off:         " + Hex.u4(mapOff));
+        }
+
+        // Write the magic number.
+        for (int i = 0; i < 8; i++) {
+            out.writeByte(MAGIC.charAt(i));
+        }
+
+        // Leave space for the checksum and signature.
+        out.writeZeroes(24);
+
+        out.writeInt(file.getFileSize());
+        out.writeInt(HEADER_SIZE);
+        out.writeInt(ENDIAN_TAG);
+
+        /*
+         * Write zeroes for the link size and data, as the output
+         * isn't a staticly linked file.
+         */
+        out.writeZeroes(8);
+
+        out.writeInt(mapOff);
+        
+        // Write out each section's respective header part.
+        file.getStringIds().writeHeaderPart(out);
+        file.getTypeIds().writeHeaderPart(out);
+        file.getProtoIds().writeHeaderPart(out);
+        file.getFieldIds().writeHeaderPart(out);
+        file.getMethodIds().writeHeaderPart(out);
+        file.getClassDefs().writeHeaderPart(out);
+
+        if (out.annotates()) {
+            out.annotate(4, "data_size:       " + Hex.u4(dataSize));
+            out.annotate(4, "data_off:        " + Hex.u4(dataOff));
+        }
+
+        out.writeInt(dataSize);
+        out.writeInt(dataOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderSection.java b/dx/src/com/android/dx/dex/file/HeaderSection.java
new file mode 100644
index 0000000..9022e0f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderSection.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * File header section of a <code>.dex</code> file.
+ */
+public final class HeaderSection extends UniformItemSection {
+    /** non-null; the list of the one item in the section */
+    private final List<HeaderItem> list;
+    
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public HeaderSection(DexFile file) {
+        super(null, file, 4);
+
+        HeaderItem item = new HeaderItem();
+        item.setIndex(0);
+
+        this.list = Collections.singletonList(item);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        // Nothing to do here.
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/IdItem.java b/dx/src/com/android/dx/dex/file/IdItem.java
new file mode 100644
index 0000000..8342514
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IdItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Representation of a reference to an item inside a Dalvik file.
+ */
+public abstract class IdItem extends IndexedItem {
+    /**
+     * non-null; the type constant for the defining class of
+     * the reference 
+     */
+    private final CstType type;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param type non-null; the type constant for the defining
+     * class of the reference
+     */
+    public IdItem(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.type = type;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        typeIds.intern(type);
+    }
+
+    /**
+     * Gets the type constant for the defining class of the
+     * reference.
+     * 
+     * @return non-null; the type constant
+     */
+    public final CstType getDefiningClass() {
+        return type;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/IndexedItem.java b/dx/src/com/android/dx/dex/file/IndexedItem.java
new file mode 100644
index 0000000..9bf7fd2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IndexedItem.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * An item in a Dalvik file which is referenced by index.
+ */
+public abstract class IndexedItem extends Item {
+    /** &gt;= -1; assigned index of the item, or <code>-1</code> if not
+     * yet assigned */
+    private int index;
+
+    /**
+     * Constructs an instance. The index is initially unassigned.
+     */
+    public IndexedItem() {
+        index = -1;
+    }
+
+    /**
+     * Gets whether or not this instance has been assigned an index.
+     * 
+     * @return <code>true</code> iff this instance has been assigned an index
+     */
+    public final boolean hasIndex() {
+        return (index >= 0);
+    }
+
+    /**
+     * Gets the item index.
+     *
+     * @return &gt;= 0; the index
+     * @throws RuntimeException thrown if the item index is not yet assigned
+     */
+    public final int getIndex() {
+        if (index < 0) {
+            throw new RuntimeException("index not yet set");
+        }
+
+        return index;
+    }
+
+    /**
+     * Sets the item index. This method may only ever be called once
+     * per instance, and this will throw a <code>RuntimeException</code> if
+     * called a second (or subsequent) time.
+     *
+     * @param index &gt;= 0; the item index
+     */
+    public final void setIndex(int index) {
+        if (this.index != -1) {
+            throw new RuntimeException("index already set");
+        }
+
+        this.index = index;
+    }
+
+    /**
+     * Gets the index of this item as a string, suitable for including in
+     * annotations.
+     * 
+     * @return non-null; the index string
+     */
+    public final String indexString() {
+        return '[' + Integer.toHexString(index) + ']';
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Item.java b/dx/src/com/android/dx/dex/file/Item.java
new file mode 100644
index 0000000..3708d45
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Item.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for any structurally-significant and (potentially)
+ * repeated piece of a Dalvik file.
+ */
+public abstract class Item {
+    /**
+     * Constructs an instance.
+     */
+    public Item() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Returns the item type for this instance.
+     * 
+     * @return non-null; the item type
+     */
+    public abstract ItemType itemType();
+    
+    /**
+     * Returns the human name for the particular type of item this
+     * instance is.
+     * 
+     * @return non-null; the name
+     */
+    public final String typeName() {
+        return itemType().toHuman();
+    }
+
+    /**
+     * Gets the size of this instance when written, in bytes.
+     * 
+     * @return &gt;= 0; the write size
+     */
+    public abstract int writeSize();
+
+    /**
+     * Populates a {@link DexFile} with items from within this instance.
+     * This will <i>not</i> add an item to the file for this instance itself
+     * (which should have been done by whatever refers to this instance).
+     * 
+     * <p><b>Note:</b> Subclasses must override this to do something
+     * appropriate.</p>
+     * 
+     * @param file non-null; the file to populate
+     */
+    public abstract void addContents(DexFile file);
+
+    /**
+     * Writes the representation of this instance to the given data section,
+     * using the given {@link DexFile} to look things up as needed.
+     * If this instance keeps track of its offset, then this method will
+     * note the written offset and will also throw an exception if this
+     * instance has already been written.
+     * 
+     * @param file non-null; the file to use for reference
+     * @param out non-null; where to write to
+     */
+    public abstract void writeTo(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ItemType.java b/dx/src/com/android/dx/dex/file/ItemType.java
new file mode 100644
index 0000000..ffa6573
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ItemType.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Enumeration of all the top-level item types.
+ */
+public enum ItemType implements ToHuman {
+    TYPE_HEADER_ITEM(               0x0000, "header_item"),
+    TYPE_STRING_ID_ITEM(            0x0001, "string_id_item"),
+    TYPE_TYPE_ID_ITEM(              0x0002, "type_id_item"),
+    TYPE_PROTO_ID_ITEM(             0x0003, "proto_id_item"),
+    TYPE_FIELD_ID_ITEM(             0x0004, "field_id_item"),
+    TYPE_METHOD_ID_ITEM(            0x0005, "method_id_item"),
+    TYPE_CLASS_DEF_ITEM(            0x0006, "class_def_item"),
+    TYPE_MAP_LIST(                  0x1000, "map_list"),
+    TYPE_TYPE_LIST(                 0x1001, "type_list"),
+    TYPE_ANNOTATION_SET_REF_LIST(   0x1002, "annotation_set_ref_list"),
+    TYPE_ANNOTATION_SET_ITEM(       0x1003, "annotation_set_item"),
+    TYPE_CLASS_DATA_ITEM(           0x2000, "class_data_item"),
+    TYPE_CODE_ITEM(                 0x2001, "code_item"),
+    TYPE_STRING_DATA_ITEM(          0x2002, "string_data_item"),
+    TYPE_DEBUG_INFO_ITEM(           0x2003, "debug_info_item"),
+    TYPE_ANNOTATION_ITEM(           0x2004, "annotation_item"),
+    TYPE_ENCODED_ARRAY_ITEM(        0x2005, "encoded_array_item"),
+    TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"),
+    TYPE_MAP_ITEM(                  -1,     "map_item"),
+    TYPE_TYPE_ITEM(                 -1,     "type_item"),
+    TYPE_EXCEPTION_HANDLER_ITEM(    -1,     "exception_handler_item"),
+    TYPE_ANNOTATION_SET_REF_ITEM(   -1,     "annotation_set_ref_item");
+    
+    /** value when represented in a {@link MapItem} */
+    private final int mapValue;
+
+    /** non-null; name of the type */
+    private final String typeName;
+
+    /** non-null; the short human name */
+    private final String humanName;
+   
+    /**
+     * Constructs an instance.
+     * 
+     * @param mapValue value when represented in a {@link MapItem}
+     * @param typeName non-null; name of the type
+     */
+    private ItemType(int mapValue, String typeName) {
+        this.mapValue = mapValue;
+        this.typeName = typeName;
+
+        // Make the human name.
+        String human = typeName;
+        if (human.endsWith("_item")) {
+            human = human.substring(0, human.length() - 5);
+        }
+        this.humanName = human.replace('_', ' ');
+    }
+
+    /**
+     * Gets the map value.
+     * 
+     * @return the map value
+     */
+    public int getMapValue() {
+        return mapValue;
+    }
+    
+    /**
+     * Gets the type name.
+     * 
+     * @return non-null; the type name
+     */
+    public String getTypeName() {
+        return typeName;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return humanName;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MapItem.java b/dx/src/com/android/dx/dex/file/MapItem.java
new file mode 100644
index 0000000..5e7465c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MapItem.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+
+/**
+ * Class that represents a map item.
+ */
+public final class MapItem extends OffsettedItem {
+    /** file alignment of this class, in bytes */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class, in bytes: three <code>uint</code>s */
+    private static final int WRITE_SIZE = (4 * 3);
+
+    /** non-null; item type this instance covers */
+    private final ItemType type;
+
+    /** non-null; section this instance covers */
+    private final Section section;
+
+    /**
+     * null-ok; first item covered or <code>null</code> if this is
+     * a self-reference
+     */
+    private final Item firstItem;
+
+    /**
+     * null-ok; last item covered or <code>null</code> if this is
+     * a self-reference
+     */
+    private final Item lastItem;
+
+    /**
+     * &gt; 0; count of items covered; <code>1</code> if this
+     * is a self-reference
+     */
+    private final int itemCount;
+
+    /**
+     * Constructs a list item with instances of this class representing
+     * the contents of the given array of sections, adding it to the
+     * given map section.
+     *
+     * @param sections non-null; the sections
+     * @param mapSection non-null; the section that the resulting map
+     * should be added to; it should be empty on entry to this method
+     */
+    public static void addMap(Section[] sections,
+            MixedItemSection mapSection) {
+        if (sections == null) {
+            throw new NullPointerException("sections == null");
+        }
+
+        if (mapSection.items().size() != 0) {
+            throw new IllegalArgumentException(
+                    "mapSection.items().size() != 0");
+        }
+        
+        ArrayList<MapItem> items = new ArrayList<MapItem>(50);
+
+        for (Section section : sections) {
+            ItemType currentType = null;
+            Item firstItem = null;
+            Item lastItem = null;
+            int count = 0;
+
+            for (Item item : section.items()) {
+                ItemType type = item.itemType();
+                if (type != currentType) {
+                    if (count != 0) {
+                        items.add(new MapItem(currentType, section,
+                                        firstItem, lastItem, count));
+                    }
+                    currentType = type;
+                    firstItem = item;
+                    count = 0;
+                }
+                lastItem = item;
+                count++;
+            }
+
+            if (count != 0) {
+                // Add a MapItem for the final items in the section.
+                items.add(new MapItem(currentType, section,
+                                firstItem, lastItem, count));
+            } else if (section == mapSection) {
+                // Add a MapItem for the self-referential section.
+                items.add(new MapItem(mapSection));
+            }
+        }
+
+        mapSection.add(
+                new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items));
+    }
+   
+    /**
+     * Constructs an instance.
+     * 
+     * @param type non-null; item type this instance covers
+     * @param section non-null; section this instance covers
+     * @param firstItem non-null; first item covered
+     * @param lastItem non-null; last item covered
+     * @param itemCount &gt; 0; count of items covered
+     */
+    private MapItem(ItemType type, Section section, Item firstItem,
+            Item lastItem, int itemCount) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (section == null) {
+            throw new NullPointerException("section == null");
+        }
+
+        if (firstItem == null) {
+            throw new NullPointerException("firstItem == null");
+        }
+
+        if (lastItem == null) {
+            throw new NullPointerException("lastItem == null");
+        }
+
+        if (itemCount <= 0) {
+            throw new IllegalArgumentException("itemCount <= 0");
+        }
+
+        this.type = type;
+        this.section = section;
+        this.firstItem = firstItem;
+        this.lastItem = lastItem;
+        this.itemCount = itemCount;
+    }
+
+    /**
+     * Constructs a self-referential instance. This instance is meant to
+     * represent the section containing the <code>map_list</code>.
+     * 
+     * @param section non-null; section this instance covers
+     */
+    private MapItem(Section section) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (section == null) {
+            throw new NullPointerException("section == null");
+        }
+
+        this.type = ItemType.TYPE_MAP_LIST;
+        this.section = section;
+        this.firstItem = null;
+        this.lastItem = null;
+        this.itemCount = 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_MAP_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(section.toString());
+        sb.append(' ');
+        sb.append(type.toHuman());
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // We have nothing to add.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toHuman() {
+        return toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int value = type.getMapValue();
+        int offset;
+
+        if (firstItem == null) {
+            offset = section.getFileOffset();
+        } else {
+            offset = section.getAbsoluteItemOffset(firstItem);
+        }
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + ' ' + type.getTypeName() +
+                    " map");
+            out.annotate(2, "  type:   " + Hex.u2(value) + " // " +
+                    type.toString());
+            out.annotate(2, "  unused: 0");
+            out.annotate(4, "  size:   " + Hex.u4(itemCount));
+            out.annotate(4, "  offset: " + Hex.u4(offset));
+        }
+
+        out.writeShort(value);
+        out.writeShort(0); // unused
+        out.writeInt(itemCount);
+        out.writeInt(offset);
+    }    
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdItem.java b/dx/src/com/android/dx/dex/file/MemberIdItem.java
new file mode 100644
index 0000000..d437152
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdItem.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a member (field or method) reference inside a
+ * Dalvik file.
+ */
+public abstract class MemberIdItem extends IdItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 8;
+
+    /** non-null; the constant for the member */
+    private final CstMemberRef cst;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param cst non-null; the constant for the member
+     */
+    public MemberIdItem(CstMemberRef cst) {
+        super(cst.getDefiningClass());
+
+        this.cst = cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        StringIdsSection stringIds = file.getStringIds();
+        stringIds.intern(getRef().getNat().getName());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(DexFile file, AnnotatedOutput out) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        StringIdsSection stringIds = file.getStringIds();
+        CstNat nat = cst.getNat();
+        int classIdx = typeIds.indexOf(getDefiningClass());
+        int nameIdx = stringIds.indexOf(nat.getName());
+        int typoidIdx = getTypoidIdx(file);
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + cst.toHuman());
+            out.annotate(2, "  class_idx: " + Hex.u2(classIdx));
+            out.annotate(2, String.format("  %-10s %s", getTypoidName() + ':',
+                            Hex.u2(typoidIdx)));
+            out.annotate(4, "  name_idx:  " + Hex.u4(nameIdx));
+        }            
+
+        out.writeShort(classIdx);
+        out.writeShort(typoidIdx);
+        out.writeInt(nameIdx);
+    }
+
+    /**
+     * Returns the index of the type-like thing associated with
+     * this item, in order that it may be written out. Subclasses must
+     * override this to get whatever it is they need to store.
+     * 
+     * @param file non-null; the file being written
+     * @return the index in question
+     */
+    protected abstract int getTypoidIdx(DexFile file);
+        
+    /**
+     * Returns the field name of the type-like thing associated with
+     * this item, for listing-generating purposes. Subclasses must override
+     * this.
+     * 
+     * @return non-null; the name in question
+     */
+    protected abstract String getTypoidName();
+
+    /**
+     * Gets the member constant.
+     * 
+     * @return non-null; the constant
+     */
+    public final CstMemberRef getRef() {
+        return cst;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
new file mode 100644
index 0000000..885b559
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Member (field or method) refs list section of a <code>.dex</code> file.
+ */
+public abstract class MemberIdsSection extends UniformItemSection {
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param name null-ok; the name of this instance, for annotation
+     * purposes
+     * @param file non-null; file that this instance is part of
+     */
+    public MemberIdsSection(String name, DexFile file) {
+        super(name, file, 4);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((MemberIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
new file mode 100644
index 0000000..175c1d2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a method and its annotations.
+ */
+public final class MethodAnnotationStruct
+        implements ToHuman, Comparable<MethodAnnotationStruct> {
+    /** non-null; the method in question */
+    private final CstMethodRef method;
+
+    /** non-null; the associated annotations */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; the method in question
+     * @param annotations non-null; the associated annotations
+     */
+    public MethodAnnotationStruct(CstMethodRef method,
+            AnnotationSetItem annotations) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.method = method;
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return method.hashCode();
+    }
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof MethodAnnotationStruct)) {
+            return false;
+        }
+        
+        return method.equals(((MethodAnnotationStruct) other).method);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(MethodAnnotationStruct other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + method.toHuman());
+            out.annotate(4, "      method_idx:      " + Hex.u4(methodIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(methodIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return method.toHuman() + ": " + annotations;
+    }
+
+    /**
+     * Gets the method this item is for.
+     * 
+     * @return non-null; the method
+     */
+    public CstMethodRef getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets the associated annotations.
+     * 
+     * @return non-null; the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations.getAnnotations();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdItem.java b/dx/src/com/android/dx/dex/file/MethodIdItem.java
new file mode 100644
index 0000000..5d78e96
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstBaseMethodRef;
+
+/**
+ * Representation of a method reference inside a Dalvik file.
+ */
+public final class MethodIdItem extends MemberIdItem {
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; the constant for the method
+     */
+    public MethodIdItem(CstBaseMethodRef method) {
+        super(method);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_METHOD_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        ProtoIdsSection protoIds = file.getProtoIds();
+        protoIds.intern(getMethodRef().getPrototype());
+    }
+
+    /**
+     * Gets the method constant.
+     * 
+     * @return non-null; the constant
+     */
+    public CstBaseMethodRef getMethodRef() {
+        return (CstBaseMethodRef) getRef();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getTypoidIdx(DexFile file) {
+        ProtoIdsSection protoIds = file.getProtoIds();
+        return protoIds.indexOf(getMethodRef().getPrototype());
+    }
+        
+    /** {@inheritDoc} */
+    @Override
+    protected String getTypoidName() {
+        return "proto_idx";
+    }    
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdsSection.java b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
new file mode 100644
index 0000000..6ba7cac
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Method refs list section of a <code>.dex</code> file.
+ */
+public final class MethodIdsSection extends MemberIdsSection {
+    /**
+     * non-null; map from method constants to {@link
+     * MethodIdItem} instances 
+     */
+    private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public MethodIdsSection(DexFile file) {
+        super("method_ids", file);
+
+        methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return methodIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        IndexedItem result = methodIds.get((CstBaseMethodRef) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = methodIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "method_ids_size: " + Hex.u4(sz));
+            out.annotate(4, "method_ids_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param method non-null; the reference to intern
+     * @return non-null; the interned reference
+     */
+    public MethodIdItem intern(CstBaseMethodRef method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        throwIfPrepared();
+
+        MethodIdItem result = methodIds.get(method);
+
+        if (result == null) {
+            result = new MethodIdItem(method);
+            methodIds.put(method, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given reference, which must have been added
+     * to this instance.
+     * 
+     * @param ref non-null; the reference to look up
+     * @return &gt;= 0; the reference's index
+     */
+    public int indexOf(CstBaseMethodRef ref) {
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        throwIfNotPrepared();
+
+        MethodIdItem item = methodIds.get(ref);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MixedItemSection.java b/dx/src/com/android/dx/dex/file/MixedItemSection.java
new file mode 100644
index 0000000..f03a9a3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MixedItemSection.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.TreeMap;
+
+/**
+ * A section of a <code>.dex</code> file which consists of a sequence of
+ * {@link OffsettedItem} objects, which may each be of a different concrete
+ * class and/or size.
+ * 
+ * <b>Note:</b> It is invalid for an item in an instance of this class to
+ * have a larger alignment requirement than the alignment of this instance.
+ */
+public final class MixedItemSection extends Section {
+    static enum SortType {
+        /** no sorting */
+        NONE,
+
+        /** sort by type only */
+        TYPE,
+
+        /** sort in class-major order, with instances sorted per-class */
+        INSTANCE;
+    };
+
+    /** non-null; sorter which sorts instances by type */
+    private static final Comparator<OffsettedItem> TYPE_SORTER =
+        new Comparator<OffsettedItem>() {
+        public int compare(OffsettedItem item1, OffsettedItem item2) {
+            ItemType type1 = item1.itemType();
+            ItemType type2 = item2.itemType();
+            return type1.compareTo(type2);
+        }
+    };
+    
+    /** non-null; the items in this part */
+    private final ArrayList<OffsettedItem> items;
+
+    /** non-null; items that have been explicitly interned */
+    private final HashMap<OffsettedItem, OffsettedItem> interns;
+
+    /** non-null; how to sort the items */
+    private final SortType sort;
+
+    /**
+     * &gt;= -1; the current size of this part, in bytes, or <code>-1</code>
+     * if not yet calculated
+     */
+    private int writeSize;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param name null-ok; the name of this instance, for annotation
+     * purposes
+     * @param file non-null; file that this instance is part of
+     * @param alignment &gt; 0; alignment requirement for the final output;
+     * must be a power of 2
+     * @param sort how the items should be sorted in the final output
+     */
+    public MixedItemSection(String name, DexFile file, int alignment,
+            SortType sort) {
+        super(name, file, alignment);
+
+        this.items = new ArrayList<OffsettedItem>(100);
+        this.interns = new HashMap<OffsettedItem, OffsettedItem>(100);
+        this.sort = sort;
+        this.writeSize = -1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return items;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        throwIfNotPrepared();
+        return writeSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getAbsoluteItemOffset(Item item) {
+        OffsettedItem oi = (OffsettedItem) item;
+        return oi.getAbsoluteOffset();
+    }
+
+    /**
+     * Gets the size of this instance, in items.
+     * 
+     * @return &gt;= 0; the size
+     */
+    public int size() {
+        return items.size();
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        if (writeSize == -1) {
+            throw new RuntimeException("write size not yet set");            
+        }
+
+        int sz = writeSize;
+        int offset = (sz == 0) ? 0 : getFileOffset();
+        String name = getName();
+
+        if (name == null) {
+            name = "<unnamed>";
+        }
+
+        int spaceCount = 15 - name.length();
+        char[] spaceArr = new char[spaceCount];
+        Arrays.fill(spaceArr, ' ');
+        String spaces = new String(spaceArr);
+
+        if (out.annotates()) {
+            out.annotate(4, name + "_size:" + spaces + Hex.u4(sz));
+            out.annotate(4, name + "_off: " + spaces + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Adds an item to this instance. This will in turn tell the given item
+     * that it has been added to this instance. It is invalid to add the
+     * same item to more than one instance, nor to add the same items
+     * multiple times to a single instance.
+     * 
+     * @param item non-null; the item to add
+     */
+    public void add(OffsettedItem item) {
+        throwIfPrepared();
+
+        try {
+            if (item.getAlignment() > getAlignment()) {
+                throw new IllegalArgumentException(
+                        "incompatible item alignment");
+            }
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("item == null");
+        }
+
+        items.add(item);
+    }
+
+    /**
+     * Interns an item in this instance, returning the interned instance
+     * (which may not be the one passed in). This will add the item if no
+     * equal item has been added.
+     * 
+     * @param item non-null; the item to intern
+     * @return non-null; the equivalent interned instance
+     */
+    public <T extends OffsettedItem> T intern(T item) {
+        throwIfPrepared();
+
+        OffsettedItem result = interns.get(item);
+        
+        if (result != null) {
+            return (T) result;
+        }
+
+        add(item);
+        interns.put(item, item);
+        return item;
+    }
+
+    /**
+     * Gets an item which was previously interned.
+     * 
+     * @param item non-null; the item to look for
+     * @return non-null; the equivalent already-interned instance
+     */
+    public <T extends OffsettedItem> T get(T item) {
+        throwIfNotPrepared();
+
+        OffsettedItem result = interns.get(item);
+        
+        if (result != null) {
+            return (T) result;
+        }
+
+        throw new NoSuchElementException(item.toString());
+    }
+
+    /**
+     * Writes an index of contents of the items in this instance of the
+     * given type. If there are none, this writes nothing. If there are any,
+     * then the index is preceded by the given intro string.
+     * 
+     * @param out non-null; where to write to
+     * @param itemType non-null; the item type of interest
+     * @param intro non-null; the introductory string for non-empty indices
+     */
+    public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType,
+            String intro) {
+        throwIfNotPrepared();
+
+        TreeMap<String, OffsettedItem> index =
+            new TreeMap<String, OffsettedItem>();
+
+        for (OffsettedItem item : items) {
+            if (item.itemType() == itemType) {
+                String label = item.toHuman();
+                index.put(label, item);
+            }
+        }
+
+        if (index.size() == 0) {
+            return;
+        }
+
+        out.annotate(0, intro);
+        
+        for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) {
+            String label = entry.getKey();
+            OffsettedItem item = entry.getValue();
+            out.annotate(0, item.offsetString() + ' ' + label + '\n');
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void prepare0() {
+        DexFile file = getFile();
+
+        /*
+         * It's okay for new items to be added as a result of an
+         * addContents() call; we just have to deal with the possibility.
+         */
+
+        int i = 0;
+        for (;;) {
+            int sz = items.size();
+            if (i >= sz) {
+                break;
+            }
+
+            for (/*i*/; i < sz; i++) {
+                OffsettedItem one = items.get(i);
+                one.addContents(file);
+            }
+        }
+    }
+
+    /**
+     * Places all the items in this instance at particular offsets. This
+     * will call {@link OffsettedItem#place} on each item. If an item
+     * does not know its write size before the call to <code>place</code>,
+     * it is that call which is responsible for setting the write size.
+     * This method may only be called once per instance; subsequent calls
+     * will throw an exception.
+     */
+    public void placeItems() {
+        throwIfNotPrepared();
+
+        switch (sort) {
+            case INSTANCE: {
+                Collections.sort(items);
+                break;
+            }
+            case TYPE: {
+                Collections.sort(items, TYPE_SORTER);
+                break;
+            }
+        }
+
+        int sz = items.size();
+        int outAt = 0;
+        for (int i = 0; i < sz; i++) {
+            OffsettedItem one = items.get(i);
+            try {
+                int placedAt = one.place(this, outAt);
+
+                if (placedAt < outAt) {
+                    throw new RuntimeException("bogus place() result for " +
+                            one);
+                }
+
+                outAt = placedAt + one.writeSize();
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while placing " + one);
+            }
+        }
+
+        writeSize = outAt;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        boolean first = true;
+        DexFile file = getFile();
+        int at = 0;
+
+        for (OffsettedItem one : items) {
+            if (annotates) {
+                if (first) {
+                    first = false;
+                } else {
+                    out.annotate(0, "\n");
+                }
+            }
+
+            int alignMask = one.getAlignment() - 1;
+            int writeAt = (at + alignMask) & ~alignMask;
+
+            if (at != writeAt) {
+                out.writeZeroes(writeAt - at);
+                at = writeAt;
+            }
+
+            one.writeTo(file, out);
+            at += one.writeSize();
+        }
+
+        if (at != writeSize) {
+            throw new RuntimeException("output size mismatch");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/OffsettedItem.java b/dx/src/com/android/dx/dex/file/OffsettedItem.java
new file mode 100644
index 0000000..030c370
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/OffsettedItem.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * An item in a Dalvik file which is referenced by absolute offset.
+ */
+public abstract class OffsettedItem extends Item
+        implements Comparable<OffsettedItem> {
+    /** &gt; 0; alignment requirement */
+    private final int alignment;
+
+    /** &gt;= -1; the size of this instance when written, in bytes, or
+     * <code>-1</code> if not yet known */
+    private int writeSize;
+
+    /**
+     * null-ok; section the item was added to, or <code>null</code> if
+     * not yet added 
+     */
+    private Section addedTo;
+
+    /**
+     * &gt;= -1; assigned offset of the item from the start of its section,
+     * or <code>-1</code> if not yet assigned 
+     */
+    private int offset;
+
+    /**
+     * Gets the absolute offset of the given item, returning <code>0</code>
+     * if handed <code>null</code>.
+     * 
+     * @param item null-ok; the item in question
+     * @return &gt;= 0; the item's absolute offset, or <code>0</code>
+     * if <code>item == null</code>
+     */
+    public static int getAbsoluteOffsetOr0(OffsettedItem item) {
+        if (item == null) {
+            return 0;
+        }
+
+        return item.getAbsoluteOffset();
+    }
+
+    /**
+     * Constructs an instance. The offset is initially unassigned.
+     * 
+     * @param alignment &gt; 0; output alignment requirement; must be a
+     * power of 2
+     * @param writeSize &gt;= -1; the size of this instance when written,
+     * in bytes, or <code>-1</code> if not immediately known
+     */
+    public OffsettedItem(int alignment, int writeSize) {
+        Section.validateAlignment(alignment);
+
+        if (writeSize < -1) {
+            throw new IllegalArgumentException("writeSize < -1");
+        }
+
+        this.alignment = alignment;
+        this.writeSize = writeSize;
+        this.addedTo = null;
+        this.offset = -1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Comparisons for this class are defined to be type-major (if the
+     * types don't match then the objects are not equal), with
+     * {@link #compareTo0} deciding same-type comparisons.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        OffsettedItem otherItem = (OffsettedItem) other;
+        ItemType thisType = itemType();
+        ItemType otherType = otherItem.itemType();
+
+        if (thisType != otherType) {
+            return false;
+        }
+
+        return (compareTo0(otherItem) == 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Comparisons for this class are defined to be class-major (if the
+     * classes don't match then the objects are not equal), with
+     * {@link #compareTo0} deciding same-class comparisons.
+     */
+    public final int compareTo(OffsettedItem other) {
+        if (this == other) {
+            return 0;
+        }
+
+        ItemType thisType = itemType();
+        ItemType otherType = other.itemType();
+
+        if (thisType != otherType) {
+            return thisType.compareTo(otherType);
+        }
+
+        return compareTo0(other);
+    }
+
+    /**
+     * Sets the write size of this item. This may only be called once
+     * per instance, and only if the size was unknown upon instance
+     * creation.
+     * 
+     * @param writeSize &gt; 0; the write size, in bytes
+     */
+    public final void setWriteSize(int writeSize) {
+        if (writeSize < 0) {
+            throw new IllegalArgumentException("writeSize < 0");
+        }
+
+        if (this.writeSize >= 0) {
+            throw new UnsupportedOperationException("writeSize already set");
+        }
+
+        this.writeSize = writeSize;
+    }
+    
+    /** {@inheritDoc} 
+     * 
+     * @throws UnsupportedOperationException thrown if the write size
+     * is not yet known
+     */
+    @Override
+    public final int writeSize() {
+        if (writeSize < 0) {
+            throw new UnsupportedOperationException("writeSize is unknown");
+        }
+                
+        return writeSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(DexFile file, AnnotatedOutput out) {
+        out.alignTo(alignment);
+
+        try {
+            if (writeSize < 0) {
+                throw new UnsupportedOperationException(
+                        "writeSize is unknown");
+            }
+            out.assertCursor(getAbsoluteOffset());
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while writing " + this);
+        }
+
+        writeTo0(file, out);
+    }
+
+    /**
+     * Gets the relative item offset. The offset is from the start of
+     * the section which the instance was written to.
+     * 
+     * @return &gt;= 0; the offset
+     * @throws RuntimeException thrown if the offset is not yet known
+     */
+    public final int getRelativeOffset() {
+        if (offset < 0) {
+            throw new RuntimeException("offset not yet known");
+        }
+
+        return offset;
+    }
+
+    /**
+     * Gets the absolute item offset. The offset is from the start of
+     * the file which the instance was written to.
+     * 
+     * @return &gt;= 0; the offset
+     * @throws RuntimeException thrown if the offset is not yet known
+     */
+    public final int getAbsoluteOffset() {
+        if (offset < 0) {
+            throw new RuntimeException("offset not yet known");
+        }
+
+        return addedTo.getAbsoluteOffset(offset);
+    }
+
+    /**
+     * Indicates that this item has been added to the given section at
+     * the given offset. It is only valid to call this method once per
+     * instance.
+     * 
+     * @param addedTo non-null; the section this instance has been added to
+     * @param offset &gt;= 0; the desired offset from the start of the
+     * section where this instance was placed
+     * @return &gt;= 0; the offset that this instance should be placed at
+     * in order to meet its alignment constraint
+     */
+    public final int place(Section addedTo, int offset) {
+        if (addedTo == null) {
+            throw new NullPointerException("addedTo == null");
+        }
+
+        if (offset < 0) {
+            throw new IllegalArgumentException("offset < 0");
+        }
+
+        if (this.addedTo != null) {
+            throw new RuntimeException("already written");
+        }
+
+        int mask = alignment - 1;
+        offset = (offset + mask) & ~mask;
+
+        this.addedTo = addedTo;
+        this.offset = offset;
+
+        place0(addedTo, offset);
+
+        return offset;
+    }
+
+    /**
+     * Gets the alignment requirement of this instance. An instance should
+     * only be written when so aligned.
+     * 
+     * @return &gt; 0; the alignment requirement; must be a power of 2
+     */
+    public final int getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Gets the absolute offset of this item as a string, suitable for
+     * including in annotations.
+     * 
+     * @return non-null; the offset string
+     */
+    public final String offsetString() {
+        return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
+    }
+
+    /**
+     * Gets a short human-readable string representing this instance.
+     * 
+     * @return non-null; the human form
+     */
+    public abstract String toHuman();
+
+    /**
+     * Compares this instance to another which is guaranteed to be of
+     * the same class. The default implementation of this method is to
+     * throw an exception (unsupported operation). If a particular
+     * class needs to actually sort, then it should override this
+     * method.
+     * 
+     * @param other non-null; instance to compare to
+     * @return <code>-1</code>, <code>0</code>, or <code>1</code>, depending
+     * on the sort order of this instance and the other
+     */
+    protected int compareTo0(OffsettedItem other) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /**
+     * Does additional work required when placing an instance. The
+     * default implementation of this method is a no-op. If a
+     * particular class needs to do something special, then it should
+     * override this method. In particular, if this instance did not
+     * know its write size up-front, then this method is responsible
+     * for setting it.
+     * 
+     * @param addedTo non-null; the section this instance has been added to
+     * @param offset &gt;= 0; the offset from the start of the
+     * section where this instance was placed
+     */
+    protected void place0(Section addedTo, int offset) {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Performs the actual write of the contents of this instance to
+     * the given data section. This is called by {@link #writeTo},
+     * which will have taken care of ensuring alignment.
+     * 
+     * @param file non-null; the file to use for reference
+     * @param out non-null; where to write to
+     */
+    protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
new file mode 100644
index 0000000..0c2d286
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+import java.util.ArrayList;
+
+/**
+ * Association of a method and its parameter annotations.
+ */
+public final class ParameterAnnotationStruct
+        implements ToHuman, Comparable<ParameterAnnotationStruct> {
+    /** non-null; the method in question */
+    private final CstMethodRef method;
+
+    /** non-null; the associated annotations list */
+    private final AnnotationsList annotationsList;
+
+    /** non-null; the associated annotations list, as an item */
+    private final UniformListItem<AnnotationSetRefItem> annotationsItem;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; the method in question
+     * @param annotationsList non-null; the associated annotations list
+     */
+    public ParameterAnnotationStruct(CstMethodRef method,
+            AnnotationsList annotationsList) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (annotationsList == null) {
+            throw new NullPointerException("annotationsList == null");
+        }
+
+        this.method = method;
+        this.annotationsList = annotationsList;
+
+        /*
+         * Construct an item for the annotations list. TODO: This
+         * requires way too much copying; fix it.
+         */
+
+        int size = annotationsList.size();
+        ArrayList<AnnotationSetRefItem> arrayList = new
+            ArrayList<AnnotationSetRefItem>(size);
+
+        for (int i = 0; i < size; i++) {
+            Annotations annotations = annotationsList.get(i);
+            AnnotationSetItem item = new AnnotationSetItem(annotations);
+            arrayList.add(new AnnotationSetRefItem(item));
+        }
+                
+        this.annotationsItem = new UniformListItem<AnnotationSetRefItem>(
+                ItemType.TYPE_ANNOTATION_SET_REF_LIST, arrayList);
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return method.hashCode();
+    }
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof ParameterAnnotationStruct)) {
+            return false;
+        }
+        
+        return method.equals(((ParameterAnnotationStruct) other).method);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(ParameterAnnotationStruct other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+        wordData.add(annotationsItem);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int annotationsOff = annotationsItem.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + method.toHuman());
+            out.annotate(4, "      method_idx:      " + Hex.u4(methodIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(methodIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(method.toHuman());
+        sb.append(": ");
+
+        boolean first = true;
+        for (AnnotationSetRefItem item : annotationsItem.getItems()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(item.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the method this item is for.
+     * 
+     * @return non-null; the method
+     */
+    public CstMethodRef getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets the associated annotations list.
+     * 
+     * @return non-null; the annotations list
+     */
+    public AnnotationsList getAnnotationsList() {
+        return annotationsList;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdItem.java b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
new file mode 100644
index 0000000..a144c30
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a method prototype reference inside a Dalvik file.
+ */
+public final class ProtoIdItem extends IndexedItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 12;
+
+    /** non-null; the wrapped prototype */
+    private final Prototype prototype;
+
+    /** non-null; the short-form of the prototype */
+    private final CstUtf8 shortForm;
+
+    /**
+     * null-ok; the list of parameter types or <code>null</code> if this
+     * prototype has no parameters
+     */
+    private TypeListItem parameterTypes;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param prototype non-null; the constant for the prototype
+     */
+    public ProtoIdItem(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        this.prototype = prototype;
+        this.shortForm = makeShortForm(prototype);
+
+        StdTypeList parameters = prototype.getParameterTypes();
+        this.parameterTypes = (parameters.size() == 0) ? null 
+            : new TypeListItem(parameters);
+    }
+
+    /**
+     * Creates the short-form of the given prototype.
+     * 
+     * @param prototype non-null; the prototype
+     * @return non-null; the short form
+     */
+    private static CstUtf8 makeShortForm(Prototype prototype) {
+        StdTypeList parameters = prototype.getParameterTypes();
+        int size = parameters.size();
+        StringBuilder sb = new StringBuilder(size + 1);
+
+        sb.append(shortFormCharFor(prototype.getReturnType()));
+
+        for (int i = 0; i < size; i++) {
+            sb.append(shortFormCharFor(parameters.getType(i)));
+        }
+
+        return new CstUtf8(sb.toString());
+    }
+
+    /**
+     * Gets the short-form character for the given type.
+     * 
+     * @param type non-null; the type
+     * @return the corresponding short-form character
+     */
+    private static char shortFormCharFor(Type type) {
+        char descriptorChar = type.getDescriptor().charAt(0);
+
+        if (descriptorChar == '[') {
+            return 'L';
+        }
+
+        return descriptorChar;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_PROTO_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        StringIdsSection stringIds = file.getStringIds();
+        TypeIdsSection typeIds = file.getTypeIds();
+        MixedItemSection typeLists = file.getTypeLists();
+
+        typeIds.intern(prototype.getReturnType());
+        stringIds.intern(shortForm);
+
+        if (parameterTypes != null) {
+            parameterTypes = typeLists.intern(parameterTypes);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int shortyIdx = file.getStringIds().indexOf(shortForm);
+        int returnIdx = file.getTypeIds().indexOf(prototype.getReturnType());
+        int paramsOff = OffsettedItem.getAbsoluteOffsetOr0(parameterTypes);
+            
+        if (out.annotates()) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(prototype.getReturnType().toHuman());
+            sb.append(" proto(");
+
+            StdTypeList params = prototype.getParameterTypes();
+            int size = params.size();
+
+            for (int i = 0; i < size; i++) {
+                if (i != 0) {
+                    sb.append(", ");
+                }
+                sb.append(params.getType(i).toHuman());
+            }
+            
+            sb.append(")");
+            out.annotate(0, indexString() + ' ' + sb.toString());
+            out.annotate(4, "  shorty_idx:      " + Hex.u4(shortyIdx) +
+                    " // " + shortForm.toQuoted());
+            out.annotate(4, "  return_type_idx: " + Hex.u4(returnIdx) +
+                    " // " + prototype.getReturnType().toHuman());
+            out.annotate(4, "  parameters_off:  " + Hex.u4(paramsOff));
+        }
+
+        out.writeInt(shortyIdx);
+        out.writeInt(returnIdx);
+        out.writeInt(paramsOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdsSection.java b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
new file mode 100644
index 0000000..852ab9d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Proto (method prototype) identifiers list section of a
+ * <code>.dex</code> file.
+ */
+public final class ProtoIdsSection extends UniformItemSection {
+    /**
+     * non-null; map from method prototypes to {@link ProtoIdItem} instances
+     */
+    private final TreeMap<Prototype, ProtoIdItem> protoIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public ProtoIdsSection(DexFile file) {
+        super("proto_ids", file, 4);
+
+        protoIds = new TreeMap<Prototype, ProtoIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return protoIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = protoIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (sz > 65536) {
+            throw new UnsupportedOperationException("too many proto ids");
+        }
+
+        if (out.annotates()) {
+            out.annotate(4, "proto_ids_size:  " + Hex.u4(sz));
+            out.annotate(4, "proto_ids_off:   " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param prototype non-null; the prototype to intern
+     * @return non-null; the interned reference
+     */
+    public ProtoIdItem intern(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        throwIfPrepared();
+
+        ProtoIdItem result = protoIds.get(prototype);
+
+        if (result == null) {
+            result = new ProtoIdItem(prototype);
+            protoIds.put(prototype, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given prototype, which must have
+     * been added to this instance.
+     * 
+     * @param prototype non-null; the prototype to look up
+     * @return &gt;= 0; the reference's index
+     */
+    public int indexOf(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        throwIfNotPrepared();
+
+        ProtoIdItem item = protoIds.get(prototype);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((ProtoIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Section.java b/dx/src/com/android/dx/dex/file/Section.java
new file mode 100644
index 0000000..9f7657c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Section.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a <code>.dex</code> file. Each section consists of a list
+ * of items of some sort or other.
+ */
+public abstract class Section {
+    /** null-ok; name of this part, for annotation purposes */
+    private final String name;
+
+    /** non-null; file that this instance is part of */
+    private final DexFile file;
+
+    /** &gt; 0; alignment requirement for the final output;
+     * must be a power of 2 */
+    private final int alignment;
+
+    /** &gt;= -1; offset from the start of the file to this part, or
+     * <code>-1</code> if not yet known */
+    private int fileOffset;
+
+    /** whether {@link #prepare} has been called successfully on this
+     * instance */
+    private boolean prepared;
+
+    /**
+     * Validates an alignment.
+     * 
+     * @param alignment the alignment
+     * @throws IllegalArgumentException thrown if <code>alignment</code>
+     * isn't a positive power of 2
+     */
+    public static void validateAlignment(int alignment) {
+        if ((alignment <= 0) ||
+            (alignment & (alignment - 1)) != 0) {
+            throw new IllegalArgumentException("invalid alignment");
+        }
+    }
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param name null-ok; the name of this instance, for annotation
+     * purposes
+     * @param file non-null; file that this instance is part of
+     * @param alignment &gt; 0; alignment requirement for the final output;
+     * must be a power of 2
+     */
+    public Section(String name, DexFile file, int alignment) {
+        if (file == null) {
+            throw new NullPointerException("file == null");
+        }
+
+        validateAlignment(alignment);
+
+        this.name = name;
+        this.file = file;
+        this.alignment = alignment;
+        this.fileOffset = -1;
+        this.prepared = false;
+    }
+
+    /**
+     * Gets the file that this instance is part of.
+     *
+     * @return non-null; the file
+     */
+    public final DexFile getFile() {
+        return file;
+    }
+
+    /** 
+     * Gets the alignment for this instance's final output.
+     * 
+     * @return &gt; 0; the alignment
+     */
+    public final int getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Gets the offset from the start of the file to this part. This
+     * throws an exception if the offset has not yet been set.
+     *
+     * @return &gt;= 0; the file offset
+     */
+    public final int getFileOffset() {
+        if (fileOffset < 0) {
+            throw new RuntimeException("fileOffset not set");
+        }
+
+        return fileOffset;
+    }
+
+    /**
+     * Sets the file offset. It is only valid to call this method once
+     * once per instance.
+     *
+     * @param fileOffset &gt;= 0; the desired offset from the start of the
+     * file where this for this instance
+     * @return &gt;= 0; the offset that this instance should be placed at
+     * in order to meet its alignment constraint
+     */
+    public final int setFileOffset(int fileOffset) {
+        if (fileOffset < 0) {
+            throw new IllegalArgumentException("fileOffset < 0");
+        }
+
+        if (this.fileOffset >= 0) {
+            throw new RuntimeException("fileOffset already set");
+        }
+
+        int mask = alignment - 1;
+        fileOffset = (fileOffset + mask) & ~mask;
+
+        this.fileOffset = fileOffset;
+
+        return fileOffset;
+    }
+
+    /**
+     * Writes this instance to the given raw data object.
+     *
+     * @param out non-null; where to write to
+     */
+    public final void writeTo(AnnotatedOutput out) {
+        throwIfNotPrepared();        
+        align(out);
+
+        int cursor = out.getCursor();
+
+        if (fileOffset < 0) {
+            fileOffset = cursor;
+        } else if (fileOffset != cursor) {
+            throw new RuntimeException("alignment mismatch: for " + this +
+                                       ", at " + cursor +
+                                       ", but expected " + fileOffset);
+        }
+
+        if (out.annotates()) {
+            if (name != null) {
+                out.annotate(0, "\n" + name + ":");
+            } else if (cursor != 0) {
+                out.annotate(0, "\n");
+            }
+        }
+
+        writeTo0(out);
+    }
+
+    /**
+     * Returns the absolute file offset, given an offset from the
+     * start of this instance's output. This is only valid to call
+     * once this instance has been assigned a file offset (via {@link
+     * #setFileOffset}).
+     * 
+     * @param relative &gt;= 0; the relative offset
+     * @return &gt;= 0; the corresponding absolute file offset
+     */
+    public final int getAbsoluteOffset(int relative) {
+        if (relative < 0) {
+            throw new IllegalArgumentException("relative < 0");
+        }
+
+        if (fileOffset < 0) {
+            throw new RuntimeException("fileOffset not yet set");
+        }
+
+        return fileOffset + relative;
+    }
+
+    /**
+     * Returns the absolute file offset of the given item which must
+     * be contained in this section. This is only valid to call
+     * once this instance has been assigned a file offset (via {@link
+     * #setFileOffset}).
+     * 
+     * <p><b>Note:</b> Subclasses must implement this as appropriate for
+     * their contents.</p>
+     * 
+     * @param item non-null; the item in question
+     * @return &gt;= 0; the item's absolute file offset
+     */
+    public abstract int getAbsoluteItemOffset(Item item);
+
+    /**
+     * Prepares this instance for writing. This performs any necessary
+     * prerequisites, including particularly adding stuff to other
+     * sections. This method may only be called once per instance;
+     * subsequent calls will throw an exception.
+     */
+    public final void prepare() {
+        throwIfPrepared();
+        prepare0();
+        prepared = true;
+    }
+
+    /**
+     * Gets the collection of all the items in this section.
+     * It is not valid to attempt to change the returned list.
+     *
+     * @return non-null; the items
+     */
+    public abstract Collection<? extends Item> items();
+
+    /**
+     * Does the main work of {@link #prepare}.
+     */
+    protected abstract void prepare0();
+
+    /**
+     * Gets the size of this instance when output, in bytes.
+     *
+     * @return &gt;= 0; the size of this instance, in bytes
+     */
+    public abstract int writeSize();
+
+    /**
+     * Throws an exception if {@link #prepare} has not been
+     * called on this instance.
+     */
+    protected final void throwIfNotPrepared() {
+        if (!prepared) {
+            throw new RuntimeException("not prepared");
+        }
+    }
+
+    /**
+     * Throws an exception if {@link #prepare} has already been called
+     * on this instance.
+     */
+    protected final void throwIfPrepared() {
+        if (prepared) {
+            throw new RuntimeException("already prepared");
+        }
+    }
+
+    /**
+     * Aligns the output of the given data to the alignment of this instance.
+     * 
+     * @param out non-null; the output to align
+     */
+    protected final void align(AnnotatedOutput out) {
+        out.alignTo(alignment);
+    }
+
+    /**
+     * Writes this instance to the given raw data object. This gets
+     * called by {@link #writeTo} after aligning the cursor of
+     * <code>out</code> and verifying that either the assigned file
+     * offset matches the actual cursor <code>out</code> or that the
+     * file offset was not previously assigned, in which case it gets
+     * assigned to <code>out</code>'s cursor.
+     * 
+     * @param out non-null; where to write to
+     */
+    protected abstract void writeTo0(AnnotatedOutput out);
+
+    /**
+     * Returns the name of this section, for annotation purposes.
+     * 
+     * @return null-ok; name of this part, for annotation purposes
+     */
+    protected final String getName() {
+        return name;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Statistics.java b/dx/src/com/android/dx/dex/file/Statistics.java
new file mode 100644
index 0000000..b11ab6e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Statistics.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Statistics about the contents of a file.
+ */
+public final class Statistics {
+    /** non-null; data about each type of item */
+    private final HashMap<String, Data> dataMap;
+
+    /**
+     * Constructs an instance.
+     */
+    public Statistics() {
+        dataMap = new HashMap<String, Data>(50);
+    }
+
+    /**
+     * Adds the given item to the statistics.
+     * 
+     * @param item non-null; the item to add
+     */
+    public void add(Item item) {
+        String typeName = item.typeName();
+        Data data = dataMap.get(typeName);
+
+        if (data == null) {
+            dataMap.put(typeName, new Data(item, typeName));
+        } else {
+            data.add(item);
+        }
+    }
+
+    /**
+     * Adds the given list of items to the statistics.
+     * 
+     * @param list non-null; the list of items to add
+     */
+    public void addAll(Section list) {
+        Collection<? extends Item> items = list.items();
+        for (Item item : items) {
+            add(item);
+        }
+    }
+
+    /**
+     * Writes the statistics as an annotation.
+     * 
+     * @param out non-null; where to write to
+     */
+    public final void writeAnnotation(AnnotatedOutput out) {
+        if (dataMap.size() == 0) {
+            return;
+        }
+
+        out.annotate(0, "\nstatistics:\n");
+
+        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+        for (Data data : dataMap.values()) {
+            sortedData.put(data.name, data);
+        }
+
+        for (Data data : sortedData.values()) {
+            data.writeAnnotation(out);
+        }
+    }
+
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("Statistics:\n");
+
+        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+        for (Data data : dataMap.values()) {
+            sortedData.put(data.name, data);
+        }
+
+        for (Data data : sortedData.values()) {
+            sb.append(data.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Statistical data about a particular class.
+     */
+    private static class Data {
+        /** non-null; name to use as a label */
+        private final String name;
+
+        /** &gt;= 0; number of instances */
+        private int count;
+
+        /** &gt;= 0; total size of instances in bytes */
+        private int totalSize;
+
+        /** &gt;= 0; largest size of any individual item */
+        private int largestSize;
+
+        /** &gt;= 0; smallest size of any individual item */
+        private int smallestSize;
+
+        /**
+         * Constructs an instance for the given item.
+         * 
+         * @param item non-null; item in question
+         * @param name non-null; type name to use
+         */
+        public Data(Item item, String name) {
+            int size = item.writeSize();
+
+            this.name = name;
+            this.count = 1;
+            this.totalSize = size;
+            this.largestSize = size;
+            this.smallestSize = size;
+        }
+
+        /**
+         * Incorporates a new item. This assumes the type name matches.
+         * 
+         * @param item non-null; item to incorporate
+         */
+        public void add(Item item) {
+            int size = item.writeSize();
+
+            count++;
+            totalSize += size;
+
+            if (size > largestSize) {
+                largestSize = size;
+            }
+
+            if (size < smallestSize) {
+                smallestSize = size;
+            }
+        }
+
+        /**
+         * Writes this instance as an annotation.
+         * 
+         * @param out non-null; where to write to
+         */
+        public void writeAnnotation(AnnotatedOutput out) {
+            out.annotate(toHuman());
+        }
+
+        /**
+         * Generates a human-readable string for this data item.
+         *
+         * @return string for human consumption.
+         */
+        public String toHuman() {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("  " + name + ": " +
+                         count + " item" + (count == 1 ? "" : "s") + "; " +
+                         totalSize + " bytes total\n");
+
+            if (smallestSize == largestSize) {
+                sb.append("    " + smallestSize + " bytes/item\n");
+            } else {
+                int average = totalSize / count;
+                sb.append("    " + smallestSize + ".." + largestSize +
+                             " bytes/item; average " + average + "\n");
+            }
+
+            return sb.toString();
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringDataItem.java b/dx/src/com/android/dx/dex/file/StringDataItem.java
new file mode 100644
index 0000000..49eea57
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringDataItem.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+/**
+ * Representation of string data for a particular string, in a Dalvik file.
+ */
+public final class StringDataItem extends OffsettedItem {
+    /** non-null; the string value */
+    private final CstUtf8 value;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param value non-null; the string value
+     */
+    public StringDataItem(CstUtf8 value) {
+        super(1, writeSize(value));
+
+        this.value = value;
+    }
+
+    /**
+     * Gets the write size for a given value.
+     * 
+     * @param value non-null; the string value
+     * @return &gt;= 2 the write size, in bytes
+     */
+    private static int writeSize(CstUtf8 value) {
+        int utf16Size = value.getUtf16Size();
+        
+        // The +1 is for the '\0' termination byte.
+        return Leb128Utils.unsignedLeb128Size(utf16Size)
+            + value.getUtf8Size() + 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_STRING_DATA_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // Nothing to do here.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo0(DexFile file, AnnotatedOutput out) {
+        ByteArray bytes = value.getBytes();
+        int utf16Size = value.getUtf16Size();
+
+        if (out.annotates()) {
+            out.annotate(Leb128Utils.unsignedLeb128Size(utf16Size), 
+                    "utf16_size: " + Hex.u4(utf16Size));
+            out.annotate(bytes.size() + 1, value.toQuoted());
+        }
+
+        out.writeUnsignedLeb128(utf16Size);
+        out.write(bytes);
+        out.writeByte(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return value.toQuoted();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        StringDataItem otherData = (StringDataItem) other;
+
+        return value.compareTo(otherData.value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdItem.java b/dx/src/com/android/dx/dex/file/StringIdItem.java
new file mode 100644
index 0000000..e80a7f8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdItem.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a string inside a Dalvik file.
+ */
+public final class StringIdItem
+        extends IndexedItem implements Comparable {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 4;
+
+    /** non-null; the string value */
+    private final CstUtf8 value;
+
+    /** null-ok; associated string data object, if known */
+    private StringDataItem data;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param value non-null; the string value
+     */
+    public StringIdItem(CstUtf8 value) {
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        this.value = value;
+        this.data = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof StringIdItem)) {
+            return false;
+        }
+
+        StringIdItem otherString = (StringIdItem) other;
+        return value.equals(otherString.value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Object other) {
+        StringIdItem otherString = (StringIdItem) other;
+        return value.compareTo(otherString.value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_STRING_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        if (data == null) {
+            // The string data hasn't yet been added, so add it.
+            MixedItemSection stringData = file.getStringData();
+            data = new StringDataItem(value);
+            stringData.add(data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int dataOff = data.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + value.toQuoted(100));
+            out.annotate(4, "  string_data_off: " + Hex.u4(dataOff));
+        }
+
+        out.writeInt(dataOff);
+    }
+
+    /**
+     * Gets the string value.
+     * 
+     * @return non-null; the value
+     */
+    public CstUtf8 getValue() {
+        return value;
+    }
+
+    /**
+     * Gets the associated data object for this instance, if known.
+     * 
+     * @return null-ok; the associated data object or <code>null</code>
+     * if not yet known
+     */
+    public StringDataItem getData() {
+        return data;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdsSection.java b/dx/src/com/android/dx/dex/file/StringIdsSection.java
new file mode 100644
index 0000000..17fbb57
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdsSection.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Strings list section of a <code>.dex</code> file.
+ */
+public final class StringIdsSection
+        extends UniformItemSection {
+    /**
+     * non-null; map from string constants to {@link
+     * StringIdItem} instances 
+     */
+    private final TreeMap<CstUtf8, StringIdItem> strings;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public StringIdsSection(DexFile file) {
+        super("string_ids", file, 4);
+
+        strings = new TreeMap<CstUtf8, StringIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return strings.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        if (cst instanceof CstString) {
+            cst = ((CstString) cst).getString();
+        }
+
+        IndexedItem result = strings.get((CstUtf8) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = strings.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "string_ids_size: " + Hex.u4(sz));
+            out.annotate(4, "string_ids_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param string non-null; the string to intern, as a regular Java
+     * <code>String</code>
+     * @return non-null; the interned string
+     */
+    public StringIdItem intern(String string) {
+        CstUtf8 utf8 = new CstUtf8(string);
+        return intern(new StringIdItem(utf8));
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param string non-null; the string to intern, as a {@link CstString}
+     * @return non-null; the interned string
+     */
+    public StringIdItem intern(CstString string) {
+        CstUtf8 utf8 = string.getString();
+        return intern(new StringIdItem(utf8));
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param string non-null; the string to intern, as a constant
+     * @return non-null; the interned string
+     */
+    public StringIdItem intern(CstUtf8 string) {
+        return intern(new StringIdItem(string));
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param string non-null; the string to intern
+     * @return non-null; the interned string
+     */
+    public StringIdItem intern(StringIdItem string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        throwIfPrepared();
+
+        CstUtf8 value = string.getValue();
+        StringIdItem already = strings.get(value);
+
+        if (already != null) {
+            return already;
+        }
+
+        strings.put(value, string);
+        return string;
+    }
+
+    /**
+     * Interns the components of a name-and-type into this instance.
+     * 
+     * @param nat non-null; the name-and-type
+     */
+    public void intern(CstNat nat) {
+        intern(nat.getName());
+        intern(nat.getDescriptor());
+    }
+
+    /**
+     * Gets the index of the given string, which must have been added
+     * to this instance.
+     * 
+     * @param string non-null; the string to look up
+     * @return &gt;= 0; the string's index
+     */
+    public int indexOf(CstUtf8 string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        throwIfNotPrepared();
+
+        StringIdItem s = strings.get(string);
+
+        if (s == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return s.getIndex();
+    }
+
+    /**
+     * Gets the index of the given string, which must have been added
+     * to this instance.
+     * 
+     * @param string non-null; the string to look up
+     * @return &gt;= 0; the string's index
+     */
+    public int indexOf(CstString string) {
+        return indexOf(string.getString());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (StringIdItem s : strings.values()) {
+            s.setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdItem.java b/dx/src/com/android/dx/dex/file/TypeIdItem.java
new file mode 100644
index 0000000..f3402e6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdItem.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a type reference inside a Dalvik file.
+ */
+public final class TypeIdItem extends IdItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 4;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param type non-null; the constant for the type
+     */
+    public TypeIdItem(CstType type) {
+        super(type);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_TYPE_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        file.getStringIds().intern(getDefiningClass().getDescriptor());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        CstType type = getDefiningClass();
+        CstUtf8 descriptor = type.getDescriptor();
+        int idx = file.getStringIds().indexOf(descriptor);
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + descriptor.toHuman());
+            out.annotate(4, "  descriptor_idx: " + Hex.u4(idx));
+        }
+
+        out.writeInt(idx);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
new file mode 100644
index 0000000..296263f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Type identifiers list section of a <code>.dex</code> file.
+ */
+public final class TypeIdsSection extends UniformItemSection {
+    /**
+     * non-null; map from types to {@link TypeIdItem} instances
+     */
+    private final TreeMap<Type, TypeIdItem> typeIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param file non-null; file that this instance is part of
+     */
+    public TypeIdsSection(DexFile file) {
+        super("type_ids", file, 4);
+
+        typeIds = new TreeMap<Type, TypeIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return typeIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        Type type = ((CstType) cst).getClassType();
+        IndexedItem result = typeIds.get(type);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     * 
+     * @param out non-null; where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = typeIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (sz > 65536) {
+            throw new UnsupportedOperationException("too many type ids");
+        }
+
+        if (out.annotates()) {
+            out.annotate(4, "type_ids_size:   " + Hex.u4(sz));
+            out.annotate(4, "type_ids_off:    " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param type non-null; the type to intern
+     * @return non-null; the interned reference
+     */
+    public TypeIdItem intern(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfPrepared();
+
+        TypeIdItem result = typeIds.get(type);
+
+        if (result == null) {
+            result = new TypeIdItem(new CstType(type));
+            typeIds.put(type, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Interns an element into this instance.
+     * 
+     * @param type non-null; the type to intern
+     * @return non-null; the interned reference
+     */
+    public TypeIdItem intern(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfPrepared();
+
+        Type typePerSe = type.getClassType();
+        TypeIdItem result = typeIds.get(typePerSe);
+
+        if (result == null) {
+            result = new TypeIdItem(type);
+            typeIds.put(typePerSe, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given type, which must have
+     * been added to this instance.
+     * 
+     * @param type non-null; the type to look up
+     * @return &gt;= 0; the reference's index
+     */
+    public int indexOf(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfNotPrepared();
+
+        TypeIdItem item = typeIds.get(type);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found: " + type);
+        }
+
+        return item.getIndex();
+    }
+
+    /**
+     * Gets the index of the given type, which must have
+     * been added to this instance.
+     * 
+     * @param type non-null; the type to look up
+     * @return &gt;= 0; the reference's index
+     */
+    public int indexOf(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        return indexOf(type.getClassType());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((TypeIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeListItem.java b/dx/src/com/android/dx/dex/file/TypeListItem.java
new file mode 100644
index 0000000..6557ca4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeListItem.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a list of class references.
+ */
+public final class TypeListItem extends OffsettedItem {
+    /** alignment requirement */
+    private static final int ALIGNMENT = 4;
+
+    /** element size in bytes */
+    private static final int ELEMENT_SIZE = 2;
+
+    /** header size in bytes */
+    private static final int HEADER_SIZE = 4;
+
+    /** non-null; the actual list */
+    private final TypeList list;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param list non-null; the actual list
+     */
+    public TypeListItem(TypeList list) {
+        super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE);
+
+        this.list = list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return StdTypeList.hashContents(list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_TYPE_LIST;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        int sz = list.size();
+
+        for (int i = 0; i < sz; i++) {
+            typeIds.intern(list.getType(i));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /**
+     * Gets the underlying list.
+     * 
+     * @return non-null; the list
+     */
+    public TypeList getList() {
+        return list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        int sz = list.size();
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + " type_list");
+            out.annotate(HEADER_SIZE, "  size: " + Hex.u4(sz));
+            for (int i = 0; i < sz; i++) {
+                Type one = list.getType(i);
+                int idx = typeIds.indexOf(one);
+                out.annotate(ELEMENT_SIZE,
+                             "  " + Hex.u2(idx) + " // " + one.toHuman());
+            }
+        }
+
+        out.writeInt(sz);
+
+        for (int i = 0; i < sz; i++) {
+            out.writeShort(typeIds.indexOf(list.getType(i)));
+        }
+    }    
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        TypeList thisList = this.list;
+        TypeList otherList = ((TypeListItem) other).list;
+
+        return StdTypeList.compareContents(thisList, otherList);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformItemSection.java b/dx/src/com/android/dx/dex/file/UniformItemSection.java
new file mode 100644
index 0000000..602bc2d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformItemSection.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a <code>.dex</code> file which consists of a sequence of
+ * {@link Item} objects. Each of the items must have the same size in
+ * the output.
+ */
+public abstract class UniformItemSection extends Section {
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     * 
+     * @param name null-ok; the name of this instance, for annotation
+     * purposes
+     * @param file non-null; file that this instance is part of
+     * @param alignment &gt; 0; alignment requirement for the final output;
+     * must be a power of 2
+     */
+    public UniformItemSection(String name, DexFile file, int alignment) {
+        super(name, file, alignment);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int writeSize() {
+        Collection<? extends Item> items = items();
+        int sz = items.size();
+
+        if (sz == 0) {
+            return 0;
+        }
+
+        // Since each item has to be the same size, we can pick any.
+        return sz * items.iterator().next().writeSize();
+    }
+
+    /**
+     * Gets the item corresponding to the given {@link Constant}. This
+     * will throw an exception if the constant is not found, including
+     * if this instance isn't the sort that maps constants to {@link
+     * IndexedItem} instances.
+     * 
+     * @param cst non-null; constant to look for
+     * @return non-null; the corresponding item found in this instance
+     */
+    public abstract IndexedItem get(Constant cst);
+
+    /** {@inheritDoc} */
+    @Override
+    protected final void prepare0() {
+        DexFile file = getFile();
+
+        orderItems();
+
+        for (Item one : items()) {
+            one.addContents(file);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final void writeTo0(AnnotatedOutput out) {
+        DexFile file = getFile();
+        int alignment = getAlignment();
+
+        for (Item one : items()) {
+            one.writeTo(file, out);
+            out.alignTo(alignment);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getAbsoluteItemOffset(Item item) {
+        /*
+         * Since all items must be the same size, we can use the size
+         * of the one we're given to calculate its offset.
+         */
+        IndexedItem ii = (IndexedItem) item;
+        int relativeOffset = ii.getIndex() * ii.writeSize();
+
+        return getAbsoluteOffset(relativeOffset);
+    }
+
+    /**
+     * Alters or picks the order for items in this instance if desired,
+     * so that subsequent calls to {@link #items} will yield a
+     * so-ordered collection. If the items in this instance are indexed,
+     * then this method should also assign indices.
+     */
+    protected abstract void orderItems();
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformListItem.java b/dx/src/com/android/dx/dex/file/UniformListItem.java
new file mode 100644
index 0000000..3af3942
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformListItem.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Class that represents a contiguous list of uniform items. Each
+ * item in the list, in particular, must have the same write size and
+ * alignment.
+ * 
+ * <p>This class inherits its alignment from its items, bumped up to
+ * <code>4</code> if the items have a looser alignment requirement. If
+ * it is more than <code>4</code>, then there will be a gap after the
+ * output list size (which is four bytes) and before the first item.</p>
+ * 
+ * @param <T> type of element contained in an instance
+ */
+public final class UniformListItem<T extends OffsettedItem>
+        extends OffsettedItem {
+    /** the size of the list header */
+    private static final int HEADER_SIZE = 4;
+
+    /** non-null; the item type */
+    private final ItemType itemType;
+    
+    /** non-null; the contents */
+    private final List<T> items;
+
+    /**
+     * Constructs an instance. It is illegal to modify the given list once
+     * it is used to construct an instance of this class.
+     * 
+     * @param itemType non-null; the type of the item
+     * @param items non-null and non-empty; list of items to represent
+     */
+    public UniformListItem(ItemType itemType, List<T> items) {
+        super(getAlignment(items), writeSize(items));
+
+        if (itemType == null) {
+            throw new NullPointerException("itemType == null");
+        }
+
+        this.items = items;
+        this.itemType = itemType;
+    }
+
+    /**
+     * Helper for {@link #UniformListItem}, which returns the alignment
+     * requirement implied by the given list. See the header comment for
+     * more details.
+     * 
+     * @param items non-null; list of items being represented
+     * @return &gt;= 4; the alignment requirement
+     */
+    private static int getAlignment(List<? extends OffsettedItem> items) {
+        try {
+            // Since they all must have the same alignment, any one will do.
+            return Math.max(HEADER_SIZE, items.get(0).getAlignment());
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("items.size() == 0");
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("items == null");
+        }            
+    }
+
+    /**
+     * Calculates the write size for the given list.
+     * 
+     * @param items non-null; the list in question
+     * @return &gt;= 0; the write size
+     */
+    private static int writeSize(List<? extends OffsettedItem> items) {
+        /*
+         * This class assumes all included items are the same size,
+         * an assumption which is verified in place0().
+         */
+        OffsettedItem first = items.get(0);
+        return (items.size() * first.writeSize()) + getAlignment(items);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return itemType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append(items);
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        for (OffsettedItem i : items) {
+            i.addContents(file);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toHuman() {
+        StringBuffer sb = new StringBuffer(100);
+        boolean first = true;
+
+        sb.append("{");
+
+        for (OffsettedItem i : items) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(i.toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Gets the underlying list of items.
+     * 
+     * @return non-null; the list
+     */
+    public final List<T> getItems() {
+        return items;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        offset += headerSize();
+
+        boolean first = true;
+        int theSize = -1;
+        int theAlignment = -1;
+
+        for (OffsettedItem i : items) {
+            int size = i.writeSize();
+            if (first) {
+                theSize = size;
+                theAlignment = i.getAlignment();
+                first = false;
+            } else {
+                if (size != theSize) {
+                    throw new UnsupportedOperationException(
+                            "item size mismatch");
+                }
+                if (i.getAlignment() != theAlignment) {
+                    throw new UnsupportedOperationException(
+                            "item alignment mismatch");
+                }
+            }
+            
+            offset = i.place(addedTo, offset) + size;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int size = items.size();
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + " " + typeName());
+            out.annotate(4, "  size: " + Hex.u4(size));
+        }
+
+        out.writeInt(size);
+
+        for (OffsettedItem i : items) {
+            i.writeTo(file, out);
+        }
+    }
+
+    /**
+     * Get the size of the header of this list.
+     * 
+     * @return &gt;= 0; the header size
+     */
+    private int headerSize() {
+        /*
+         * Because of how this instance was set up, this is the same
+         * as the alignment.
+         */
+        return getAlignment();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ValueEncoder.java b/dx/src/com/android/dx/dex/file/ValueEncoder.java
new file mode 100644
index 0000000..02a3419
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+
+/**
+ * Handler for writing out <code>encoded_values</code> and parts
+ * thereof.
+ */
+public final class ValueEncoder {
+    /** annotation value type constant: <code>byte</code> */
+    private static final int VALUE_BYTE = 0x00;
+
+    /** annotation value type constant: <code>short</code> */
+    private static final int VALUE_SHORT = 0x02;
+
+    /** annotation value type constant: <code>char</code> */
+    private static final int VALUE_CHAR = 0x03;
+
+    /** annotation value type constant: <code>int</code> */
+    private static final int VALUE_INT = 0x04;
+
+    /** annotation value type constant: <code>long</code> */
+    private static final int VALUE_LONG = 0x06;
+
+    /** annotation value type constant: <code>float</code> */
+    private static final int VALUE_FLOAT = 0x10;
+
+    /** annotation value type constant: <code>double</code> */
+    private static final int VALUE_DOUBLE = 0x11;
+
+    /** annotation value type constant: <code>string</code> */
+    private static final int VALUE_STRING = 0x17;
+
+    /** annotation value type constant: <code>type</code> */
+    private static final int VALUE_TYPE = 0x18;
+
+    /** annotation value type constant: <code>field</code> */
+    private static final int VALUE_FIELD = 0x19;
+
+    /** annotation value type constant: <code>method</code> */
+    private static final int VALUE_METHOD = 0x1a;
+
+    /** annotation value type constant: <code>enum</code> */
+    private static final int VALUE_ENUM = 0x1b;
+
+    /** annotation value type constant: <code>array</code> */
+    private static final int VALUE_ARRAY = 0x1c;
+
+    /** annotation value type constant: <code>annotation</code> */
+    private static final int VALUE_ANNOTATION = 0x1d;
+
+    /** annotation value type constant: <code>null</code> */
+    private static final int VALUE_NULL = 0x1e;
+
+    /** annotation value type constant: <code>boolean</code> */
+    private static final int VALUE_BOOLEAN = 0x1f;
+
+    /** non-null; file being written */
+    private final DexFile file;
+
+    /** non-null; output stream to write to */
+    private final AnnotatedOutput out;
+    
+    /**
+     * Construct an instance.
+     * 
+     * @param file non-null; file being written
+     * @param out non-null; output stream to write to
+     */
+    public ValueEncoder(DexFile file, AnnotatedOutput out) {
+        if (file == null) {
+            throw new NullPointerException("file == null");
+        }
+
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        this.file = file;
+        this.out = out;
+    }
+
+    /**
+     * Writes out the encoded form of the given constant.
+     * 
+     * @param cst non-null; the constant to write
+     */
+    public void writeConstant(Constant cst) {
+        int type = constantToValueType(cst);
+        int arg;
+
+        switch (type) {
+            case VALUE_BYTE:
+            case VALUE_SHORT:
+            case VALUE_INT:
+            case VALUE_LONG: {
+                long value = ((CstLiteralBits) cst).getLongBits();
+                writeSignedIntegralValue(type, value);
+                break;
+            }
+            case VALUE_CHAR: {
+                long value = ((CstLiteralBits) cst).getLongBits();
+                writeUnsignedIntegralValue(type, value);
+                break;
+            }
+            case VALUE_FLOAT: {
+                // Shift value left 32 so that right-zero-extension works.
+                long value = ((CstFloat) cst).getLongBits() << 32;
+                writeRightZeroExtendedValue(type, value);
+                break;
+            }
+            case VALUE_DOUBLE: {
+                long value = ((CstDouble) cst).getLongBits();
+                writeRightZeroExtendedValue(type, value);
+                break;
+            }
+            case VALUE_STRING: {
+                int index = file.getStringIds().indexOf((CstString) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_TYPE: {
+                int index = file.getTypeIds().indexOf((CstType) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_FIELD: {
+                int index = file.getFieldIds().indexOf((CstFieldRef) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_METHOD: {
+                int index = file.getMethodIds().indexOf((CstMethodRef) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_ENUM: {
+                CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
+                int index = file.getFieldIds().indexOf(fieldRef);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_ARRAY: {
+                out.writeByte(type);
+                writeArray((CstArray) cst, false);
+                break;
+            }
+            case VALUE_ANNOTATION: {
+                out.writeByte(type);
+                writeAnnotation(((CstAnnotation) cst).getAnnotation(),
+                        false);
+                break;
+            }
+            case VALUE_NULL: {
+                out.writeByte(type);
+                break;
+            }
+            case VALUE_BOOLEAN: {
+                int value = ((CstBoolean) cst).getIntBits();
+                out.writeByte(type | (value << 5));
+                break;
+            }
+            default: {
+                throw new RuntimeException("Shouldn't happen");
+            }
+        }
+    }
+
+    /**
+     * Gets the value type for the given constant.
+     * 
+     * @param cst non-null; the constant
+     * @return the value type; one of the <code>VALUE_*</code> constants
+     * defined by this class
+     */
+    private static int constantToValueType(Constant cst) {
+        /*
+         * TODO: Constant should probable have an associated enum, so this
+         * can be a switch().
+         */
+        if (cst instanceof CstByte) {
+            return VALUE_BYTE;
+        } else if (cst instanceof CstShort) {
+            return VALUE_SHORT;
+        } else if (cst instanceof CstChar) {
+            return VALUE_CHAR;
+        } else if (cst instanceof CstInteger) {
+            return VALUE_INT;
+        } else if (cst instanceof CstLong) {
+            return VALUE_LONG;
+        } else if (cst instanceof CstFloat) {
+            return VALUE_FLOAT;
+        } else if (cst instanceof CstDouble) {
+            return VALUE_DOUBLE;
+        } else if (cst instanceof CstString) {
+            return VALUE_STRING;
+        } else if (cst instanceof CstType) {
+            return VALUE_TYPE;
+        } else if (cst instanceof CstFieldRef) {
+            return VALUE_FIELD;
+        } else if (cst instanceof CstMethodRef) {
+            return VALUE_METHOD;
+        } else if (cst instanceof CstEnumRef) {
+            return VALUE_ENUM;
+        } else if (cst instanceof CstArray) {
+            return VALUE_ARRAY;
+        } else if (cst instanceof CstAnnotation) {
+            return VALUE_ANNOTATION;
+        } else if (cst instanceof CstKnownNull) {
+            return VALUE_NULL;
+        } else if (cst instanceof CstBoolean) {
+            return VALUE_BOOLEAN;
+        } else {
+            throw new RuntimeException("Shouldn't happen");
+        }
+    }
+
+    /**
+     * Writes out the encoded form of the given array, that is, as
+     * an <code>encoded_array</code> and not including a
+     * <code>value_type</code> prefix. If the output stream keeps
+     * (debugging) annotations and <code>topLevel</code> is
+     * <code>true</code>, then this method will write (debugging)
+     * annotations.
+     *
+     * @param array non-null; array instance to write
+     * @param topLevel <code>true</code> iff the given annotation is the
+     * top-level annotation or <code>false</code> if it is a sub-annotation
+     * of some other annotation
+     */
+    public void writeArray(CstArray array, boolean topLevel) {
+        boolean annotates = topLevel && out.annotates();
+        CstArray.List list = ((CstArray) array).getList();
+        int size = list.size();
+
+        if (annotates) {
+            out.annotate("  size: " + Hex.u4(size));
+        }
+        
+        out.writeUnsignedLeb128(size);
+
+        for (int i = 0; i < size; i++) {
+            Constant cst = list.get(i);
+            if (annotates) {
+                out.annotate("  [" + Integer.toHexString(i) + "] " +
+                        constantToHuman(cst));
+            }
+            writeConstant(cst);
+        }
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+
+    /**
+     * Writes out the encoded form of the given annotation, that is,
+     * as an <code>encoded_annotation</code> and not including a
+     * <code>value_type</code> prefix. If the output stream keeps
+     * (debugging) annotations and <code>topLevel</code> is
+     * <code>true</code>, then this method will write (debugging)
+     * annotations.
+     * 
+     * @param annotation non-null; annotation instance to write
+     * @param topLevel <code>true</code> iff the given annotation is the
+     * top-level annotation or <code>false</code> if it is a sub-annotation
+     * of some other annotation
+     */
+    public void writeAnnotation(Annotation annotation, boolean topLevel) {
+        boolean annotates = topLevel && out.annotates();
+        StringIdsSection stringIds = file.getStringIds();
+        TypeIdsSection typeIds = file.getTypeIds();
+
+        CstType type = annotation.getType();
+        int typeIdx = typeIds.indexOf(type);
+
+        if (annotates) {
+            out.annotate("  type_idx: " + Hex.u4(typeIdx) + " // " +
+                    type.toHuman());
+        }
+                    
+        out.writeUnsignedLeb128(typeIds.indexOf(annotation.getType()));
+
+        Collection<NameValuePair> pairs = annotation.getNameValuePairs();
+        int size = pairs.size();
+
+        if (annotates) {
+            out.annotate("  size: " + Hex.u4(size));
+        }
+
+        out.writeUnsignedLeb128(size);
+
+        int at = 0;
+        for (NameValuePair pair : pairs) {
+            CstUtf8 name = pair.getName();
+            int nameIdx = stringIds.indexOf(name);
+            Constant value = pair.getValue();
+            
+            if (annotates) {
+                out.annotate(0, "  elements[" + at + "]:");
+                at++;
+                out.annotate("    name_idx: " + Hex.u4(nameIdx) + " // " +
+                        name.toHuman());
+            }
+
+            out.writeUnsignedLeb128(nameIdx);
+
+            if (annotates) {
+                out.annotate("    value: " + constantToHuman(value));
+            }
+
+            writeConstant(value);
+        }
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+    
+    /**
+     * Gets the colloquial type name and human form of the type of the
+     * given constant, when used as an encoded value.
+     * 
+     * @param cst non-null; the constant
+     * @return non-null; its type name and human form
+     */
+    public static String constantToHuman(Constant cst) {
+        int type = constantToValueType(cst);
+
+        if (type == VALUE_NULL) {
+            return "null";
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(cst.typeName());
+        sb.append(' ');
+        sb.append(cst.toHuman());
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out the value
+     * for any signed integral type.
+     * 
+     * @param type the type constant
+     * @param value <code>long</code> bits of the value
+     */
+    private void writeSignedIntegralValue(int type, long value) {
+        /*
+         * Figure out how many bits are needed to represent the value,
+         * including a sign bit: The bit count is subtracted from 65
+         * and not 64 to account for the sign bit. The xor operation
+         * has the effect of leaving non-negative values alone and
+         * unary complementing negative values (so that a leading zero
+         * count always returns a useful number for our present
+         * purpose).
+         */
+        int requiredBits =
+            65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out the value
+     * for any unsigned integral type.
+     * 
+     * @param type the type constant
+     * @param value <code>long</code> bits of the value
+     */
+    private void writeUnsignedIntegralValue(int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+        
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out a
+     * right-zero-extended value.
+     * 
+     * @param type the type constant
+     * @param value <code>long</code> bits of the value
+     */
+    private void writeRightZeroExtendedValue(int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+        
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        // Scootch the first bits to be written down to the low-order bits.
+        value >>= 64 - (requiredBytes * 8);
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+
+    /**
+     * Helper for <code>addContents()</code> methods, which adds
+     * contents for a particular {@link Annotation}, calling itself
+     * recursively should it encounter a nested annotation.
+     *
+     * @param file non-null; the file to add to 
+     * @param annotation non-null; the annotation to add contents for
+     */
+    public static void addContents(DexFile file, Annotation annotation) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        StringIdsSection stringIds = file.getStringIds();
+
+        typeIds.intern(annotation.getType());
+        
+        for (NameValuePair pair : annotation.getNameValuePairs()) {
+            stringIds.intern(pair.getName());
+            addContents(file, pair.getValue());
+        }
+    }
+
+    /**
+     * Helper for <code>addContents()</code> methods, which adds
+     * contents for a particular constant, calling itself recursively
+     * should it encounter a {@link CstArray} and calling {@link
+     * #addContents(DexFile,Annotation)} recursively should it
+     * encounter a {@link CstAnnotation}.
+     * 
+     * @param file non-null; the file to add to 
+     * @param cst non-null; the constant to add contents for
+     */
+    public static void addContents(DexFile file, Constant cst) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        StringIdsSection stringIds = file.getStringIds();
+
+        if (cst instanceof CstAnnotation) {
+            addContents(file, ((CstAnnotation) cst).getAnnotation());
+        } else if (cst instanceof CstArray) {
+            CstArray.List list = ((CstArray) cst).getList();
+            int size = list.size();
+            for (int i = 0; i < size; i++) {
+                addContents(file, list.get(i));
+            }
+        } else {
+            file.internIfAppropriate(cst);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotation.java b/dx/src/com/android/dx/rop/annotation/Annotation.java
new file mode 100644
index 0000000..b7cf164
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotation.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * An annotation on an element of a class. Annotations have an
+ * associated type and additionally consist of a set of (name, value)
+ * pairs, where the names are unique.
+ */
+public final class Annotation extends MutabilityControl 
+        implements Comparable<Annotation>, ToHuman {
+    /** non-null; type of the annotation */
+    private final CstType type;
+
+    /** non-null; the visibility of the annotation */
+    private final AnnotationVisibility visibility;
+
+    /** non-null; map from names to {@link NameValuePair} instances */
+    private final TreeMap<CstUtf8, NameValuePair> elements;
+    
+    /**
+     * Construct an instance. It initially contains no elements.
+     * 
+     * @param type non-null; type of the annotation
+     * @param visibility non-null; the visibility of the annotation
+     */
+    public Annotation(CstType type, AnnotationVisibility visibility) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (visibility == null) {
+            throw new NullPointerException("visibility == null");
+        }
+
+        this.type = type;
+        this.visibility = visibility;
+        this.elements = new TreeMap<CstUtf8, NameValuePair>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof Annotation)) {
+            return false;
+        }
+
+        Annotation otherAnnotation = (Annotation) other;
+
+        if (! (type.equals(otherAnnotation.type)
+                        && (visibility == otherAnnotation.visibility))) {
+            return false;
+        }
+
+        return elements.equals(otherAnnotation.elements);
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        int hash = type.hashCode();
+        hash = (hash * 31) + elements.hashCode();
+        hash = (hash * 31) + visibility.hashCode();
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Annotation other) {
+        int result = type.compareTo(other.type);
+
+        if (result != 0) {
+            return result;
+        }
+
+        result = visibility.compareTo(other.visibility);
+
+        if (result != 0) {
+            return result;
+        }
+
+        Iterator<NameValuePair> thisIter = elements.values().iterator();
+        Iterator<NameValuePair> otherIter = other.elements.values().iterator();
+
+        while (thisIter.hasNext() && otherIter.hasNext()) {
+            NameValuePair thisOne = thisIter.next();
+            NameValuePair otherOne = otherIter.next();
+
+            result = thisOne.compareTo(otherOne);
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisIter.hasNext()) {
+            return 1;
+        } else if (otherIter.hasNext()) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toHuman();
+    }
+    
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(visibility.toHuman());
+        sb.append("-annotation ");
+        sb.append(type.toHuman());
+        sb.append(" {");
+
+        boolean first = true;
+        for (NameValuePair pair : elements.values()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(pair.getName().toHuman());
+            sb.append(": ");
+            sb.append(pair.getValue().toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Gets the type of this instance.
+     * 
+     * @return non-null; the type
+     */
+    public CstType getType() {
+        return type;
+    }
+
+    /**
+     * Gets the visibility of this instance.
+     * 
+     * @return non-null; the visibility
+     */
+    public AnnotationVisibility getVisibility() {
+        return visibility;
+    }
+
+    /**
+     * Put an element into the set of (name, value) pairs for this instance.
+     * If there is a preexisting element with the same name, it will be
+     * replaced by this method.
+     * 
+     * @param pair non-null; the (name, value) pair to place into this instance
+     */
+    public void put(NameValuePair pair) {
+        throwIfImmutable();
+        
+        if (pair == null) {
+            throw new NullPointerException("pair == null");
+        }
+
+        elements.put(pair.getName(), pair);
+    }
+
+    /**
+     * Add an element to the set of (name, value) pairs for this instance.
+     * It is an error to call this method if there is a preexisting element
+     * with the same name.
+     * 
+     * @param pair non-null; the (name, value) pair to add to this instance
+     */
+    public void add(NameValuePair pair) {
+        throwIfImmutable();
+        
+        if (pair == null) {
+            throw new NullPointerException("pair == null");
+        }
+
+        CstUtf8 name = pair.getName();
+
+        if (elements.get(name) != null) {
+            throw new IllegalArgumentException("name already added: " + name);
+        }
+        
+        elements.put(name, pair);
+    }
+
+    /**
+     * Gets the set of name-value pairs contained in this instance. The
+     * result is always unmodifiable.
+     * 
+     * @return non-null; the set of name-value pairs
+     */
+    public Collection<NameValuePair> getNameValuePairs() {
+        return Collections.unmodifiableCollection(elements.values());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
new file mode 100644
index 0000000..c53fcd8
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Visibility scope of an annotation.
+ */
+public enum AnnotationVisibility implements ToHuman {
+    RUNTIME("runtime"),
+    BUILD("build"),
+    SYSTEM("system"),
+    EMBEDDED("embedded");
+
+    /** non-null; the human-oriented string representation */
+    private final String human;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param human non-null; the human-oriented string representation
+     */
+    private AnnotationVisibility(String human) {
+        this.human = human;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return human;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotations.java b/dx/src/com/android/dx/rop/annotation/Annotations.java
new file mode 100644
index 0000000..c1da883
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotations.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * List of {@link Annotation} instances.
+ */
+public final class Annotations extends MutabilityControl 
+        implements Comparable<Annotations> {
+    /** non-null; immutable empty instance */
+    public static final Annotations EMPTY = new Annotations();
+
+    static {
+        EMPTY.setImmutable();
+    }
+    
+    /** non-null; map from types to annotations */
+    private final TreeMap<CstType, Annotation> annotations;
+
+    /**
+     * Constructs an immutable instance which is the combination of the
+     * two given instances. The two instances must contain disjoint sets
+     * of types.
+     * 
+     * @param a1 non-null; an instance
+     * @param a2 non-null; the other instance
+     * @return non-null; the combination
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public static Annotations combine(Annotations a1, Annotations a2) {
+        Annotations result = new Annotations();
+
+        result.addAll(a1);
+        result.addAll(a2);
+        result.setImmutable();
+
+        return result;
+    }
+            
+    /**
+     * Constructs an immutable instance which is the combination of the
+     * given instance with the given additional annotation. The latter's
+     * type must not already appear in the former.
+     * 
+     * @param annotations non-null; the instance to augment
+     * @param annotation non-null; the additional annotation
+     * @return non-null; the combination
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public static Annotations combine(Annotations annotations,
+            Annotation annotation) {
+        Annotations result = new Annotations();
+
+        result.addAll(annotations);
+        result.add(annotation);
+        result.setImmutable();
+
+        return result;
+    }
+            
+    /**
+     * Constructs an empty instance.
+     */
+    public Annotations() {
+        annotations = new TreeMap<CstType, Annotation>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotations.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof Annotations)) {
+            return false;
+        }
+
+        Annotations otherAnnotations = (Annotations) other;
+
+        return annotations.equals(otherAnnotations.annotations);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Annotations other) {
+        Iterator<Annotation> thisIter = annotations.values().iterator();
+        Iterator<Annotation> otherIter = other.annotations.values().iterator();
+
+        while (thisIter.hasNext() && otherIter.hasNext()) {
+            Annotation thisOne = thisIter.next();
+            Annotation otherOne = otherIter.next();
+
+            int result = thisOne.compareTo(otherOne);
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisIter.hasNext()) {
+            return 1;
+        } else if (otherIter.hasNext()) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+
+        sb.append("annotations{");
+
+        for (Annotation a : annotations.values()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(a.toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+    
+    /**
+     * Gets the number of elements in this instance.
+     * 
+     * @return &gt;= 0; the size
+     */
+    public int size() {
+        return annotations.size();
+    }
+
+    /**
+     * Adds an element to this instance. There must not already be an
+     * element of the same type.
+     * 
+     * @param annotation non-null; the element to add
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public void add(Annotation annotation) {
+        throwIfImmutable();
+
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        CstType type = annotation.getType();
+        
+        if (annotations.containsKey(type)) {
+            throw new IllegalArgumentException("duplicate type: " +
+                    type.toHuman());
+        }
+
+        annotations.put(type, annotation);
+    }
+
+    /**
+     * Adds all of the elements of the given instance to this one. The
+     * instances must not have any duplicate types.
+     * 
+     * @param toAdd non-null; the annotations to add
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public void addAll(Annotations toAdd) {
+        throwIfImmutable();
+
+        if (toAdd == null) {
+            throw new NullPointerException("toAdd == null");
+        }
+
+        for (Annotation a : toAdd.annotations.values()) {
+            add(a);
+        }
+    }
+
+    /**
+     * Gets the set of annotations contained in this instance. The
+     * result is always unmodifiable.
+     * 
+     * @return non-null; the set of annotations
+     */
+    public Collection<Annotation> getAnnotations() {
+        return Collections.unmodifiableCollection(annotations.values());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationsList.java b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
new file mode 100644
index 0000000..43a07ba
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Annotations} instances.
+ */
+public final class AnnotationsList
+        extends FixedSizeList {
+    /** non-null; immutable empty instance */
+    public static final AnnotationsList EMPTY = new AnnotationsList(0);
+    
+    /**
+     * Constructs an immutable instance which is the combination of
+     * the two given instances. The two instances must each have the
+     * same number of elements, and each pair of elements must contain
+     * disjoint sets of types.
+     * 
+     * @param list1 non-null; an instance
+     * @param list2 non-null; the other instance
+     * @return non-null; the combination
+     */
+    public static AnnotationsList combine(AnnotationsList list1,
+            AnnotationsList list2) {
+        int size = list1.size();
+
+        if (size != list2.size()) {
+            throw new IllegalArgumentException("list1.size() != list2.size()");
+        }
+
+        AnnotationsList result = new AnnotationsList(size);
+
+        for (int i = 0; i < size; i++) {
+            Annotations a1 = list1.get(i);
+            Annotations a2 = list2.get(i);
+            result.set(i, Annotations.combine(a1, a2));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public AnnotationsList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Annotations get(int n) {
+        return (Annotations) get0(n);
+    }
+
+    /**
+     * Sets the element at the given index. The given element must be
+     * immutable.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param a null-ok; the element to set at <code>n</code>
+     */
+    public void set(int n, Annotations a) {
+        a.throwIfMutable();
+        set0(n, a);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/NameValuePair.java b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
new file mode 100644
index 0000000..dadabaa
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A (name, value) pair. These are used as the contents of an annotation.
+ */
+public final class NameValuePair implements Comparable<NameValuePair> {
+    /** non-null; the name */
+    private final CstUtf8 name;
+
+    /** non-null; the value */
+    private final Constant value;
+    
+    /**
+     * Construct an instance.
+     * 
+     * @param name non-null; the name
+     * @param value non-null; the value
+     */
+    public NameValuePair(CstUtf8 name, Constant value) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        // Reject CstUtf8 values. (They should be CstStrings.)
+        if (value instanceof CstUtf8) {
+            throw new IllegalArgumentException("bad value: " + value);
+        }
+        
+        this.name = name;
+        this.value = value;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        return name.toHuman() + ":" + value;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return name.hashCode() * 31 + value.hashCode();
+    }
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof NameValuePair)) {
+            return false;
+        }
+
+        NameValuePair otherPair = (NameValuePair) other;
+
+        return name.equals(otherPair.name)
+            && value.equals(otherPair.value);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * <p>Instances of this class compare in name-major and value-minor
+     * order.</p>
+     */
+    public int compareTo(NameValuePair other) {
+        int result = name.compareTo(other.name);
+
+        if (result != 0) {
+            return result;
+        }
+
+        return value.compareTo(other.value);
+    }    
+
+    /**
+     * Gets the name.
+     * 
+     * @return non-null; the name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+    
+    /**
+     * Gets the value.
+     * 
+     * @return non-null; the valute
+     */
+    public Constant getValue() {
+        return value;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/AccessFlags.java b/dx/src/com/android/dx/rop/code/AccessFlags.java
new file mode 100644
index 0000000..265cfa6
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/AccessFlags.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants used as "access flags" in various places in classes, and
+ * related utilities. Although, at the rop layer, flags are generally
+ * ignored, this is the layer of communication, and as such, this
+ * package is where these definitions belong. The flag definitions are
+ * identical to Java access flags, but <code>ACC_SUPER</code> isn't
+ * used at all in translated code, and <code>ACC_SYNCHRONIZED</code>
+ * is only used in a very limited way.
+ */
+public final class AccessFlags {
+    /** public member / class */
+    public static final int ACC_PUBLIC = 0x0001;
+
+    /** private member */
+    public static final int ACC_PRIVATE = 0x0002;
+
+    /** protected member */
+    public static final int ACC_PROTECTED = 0x0004;
+
+    /** static member */
+    public static final int ACC_STATIC = 0x0008;
+
+    /** final member / class */
+    public static final int ACC_FINAL = 0x0010;
+
+    /**
+     * synchronized method; only valid in dex files for <code>native</code>
+     * methods
+     */
+    public static final int ACC_SYNCHRONIZED = 0x0020;
+
+    /**
+     * class with new-style <code>invokespecial</code> for superclass
+     * method access 
+     */
+    public static final int ACC_SUPER = 0x0020;
+
+    /** volatile field */
+    public static final int ACC_VOLATILE = 0x0040;
+
+    /** bridge method (generated) */
+    public static final int ACC_BRIDGE = 0x0040;
+
+    /** transient field */
+    public static final int ACC_TRANSIENT = 0x0080;
+
+    /** varargs method */
+    public static final int ACC_VARARGS = 0x0080;
+
+    /** native method */
+    public static final int ACC_NATIVE = 0x0100;
+
+    /** "class" is in fact an public static final interface */
+    public static final int ACC_INTERFACE = 0x0200;
+
+    /** abstract method / class */
+    public static final int ACC_ABSTRACT = 0x0400;
+
+    /**
+     * method with strict floating point (<code>strictfp</code>)
+     * behavior 
+     */
+    public static final int ACC_STRICT = 0x0800;
+
+    /** synthetic member */
+    public static final int ACC_SYNTHETIC = 0x1000;
+
+    /** class is an annotation type */
+    public static final int ACC_ANNOTATION = 0x2000;
+
+    /**
+     * class is an enumerated type; field is an element of an enumerated
+     * type 
+     */
+    public static final int ACC_ENUM = 0x4000;
+
+    /** method is a constructor */
+    public static final int ACC_CONSTRUCTOR = 0x10000;
+
+    /**
+     * method was declared <code>synchronized</code>; has no effect on
+     * execution (other than inspecting this flag, per se)
+     */
+    public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
+
+    /** flags defined on classes */
+    public static final int CLASS_FLAGS =
+        ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT |
+        ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM;
+
+    /** flags defined on inner classes */
+    public static final int INNER_CLASS_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION |
+        ACC_ENUM;
+
+    /** flags defined on fields */
+    public static final int FIELD_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM;
+
+    /** flags defined on methods */
+    public static final int METHOD_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE |
+        ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR |
+        ACC_DECLARED_SYNCHRONIZED;
+
+    /** indicates conversion of class flags */
+    private static final int CONV_CLASS = 1;
+
+    /** indicates conversion of field flags */
+    private static final int CONV_FIELD = 2;
+
+    /** indicates conversion of method flags */
+    private static final int CONV_METHOD = 3;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private AccessFlags() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on classes (not fields or methods).
+     * 
+     * @param flags the flags
+     * @return non-null; human-oriented string
+     */
+    public static String classString(int flags) {
+        return humanHelper(flags, CLASS_FLAGS, CONV_CLASS);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on inner classes.
+     * 
+     * @param flags the flags
+     * @return non-null; human-oriented string
+     */
+    public static String innerClassString(int flags) {
+        return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on fields (not classes or methods).
+     * 
+     * @param flags the flags
+     * @return non-null; human-oriented string
+     */
+    public static String fieldString(int flags) {
+        return humanHelper(flags, FIELD_FLAGS, CONV_FIELD);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on methods (not classes or fields).
+     * 
+     * @param flags the flags
+     * @return non-null; human-oriented string
+     */
+    public static String methodString(int flags) {
+        return humanHelper(flags, METHOD_FLAGS, CONV_METHOD);
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_PUBLIC</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_PUBLIC</code> flag
+     */
+    public static boolean isPublic(int flags) {
+        return (flags & ACC_PUBLIC) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_PROTECTED</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_PROTECTED</code> flag
+     */
+    public static boolean isProtected(int flags) {
+        return (flags & ACC_PROTECTED) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_PRIVATE</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_PRIVATE</code> flag
+     */
+    public static boolean isPrivate(int flags) {
+        return (flags & ACC_PRIVATE) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_STATIC</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_STATIC</code> flag
+     */
+    public static boolean isStatic(int flags) {
+        return (flags & ACC_STATIC) != 0;
+    }
+    
+    /**
+     * Returns whether the flag <code>ACC_SYNCHRONIZED</code> is on in
+     * the given flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_SYNCHRONIZED</code> flag
+     */
+    public static boolean isSynchronized(int flags) {
+        return (flags & ACC_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_ABSTRACT</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_ABSTRACT</code> flag
+     */
+    public static boolean isAbstract(int flags) {
+        return (flags & ACC_ABSTRACT) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_NATIVE</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_NATIVE</code> flag
+     */
+    public static boolean isNative(int flags) {
+        return (flags & ACC_NATIVE) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_ANNOTATION</code> is on in the given
+     * flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_ANNOTATION</code> flag
+     */
+    public static boolean isAnnotation(int flags) {
+        return (flags & ACC_ANNOTATION) != 0;
+    }
+
+    /**
+     * Returns whether the flag <code>ACC_DECLARED_SYNCHRONIZED</code> is
+     * on in the given flags.
+     * 
+     * @param flags the flags to check
+     * @return the value of the <code>ACC_DECLARED_SYNCHRONIZED</code> flag
+     */
+    public static boolean isDeclaredSynchronized(int flags) {
+        return (flags & ACC_DECLARED_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Helper to return a human-oriented string representing the given
+     * access flags.
+     * 
+     * @param flags the defined flags
+     * @param mask mask for the "defined" bits
+     * @param what what the flags represent (one of <code>CONV_*</code>)
+     * @return non-null; human-oriented string
+     */
+    private static String humanHelper(int flags, int mask, int what) {
+        StringBuffer sb = new StringBuffer(80);
+        int extra = flags & ~mask;
+
+        flags &= mask;
+
+        if ((flags & ACC_PUBLIC) != 0) {
+            sb.append("|public");
+        }
+        if ((flags & ACC_PRIVATE) != 0) {
+            sb.append("|private");
+        }
+        if ((flags & ACC_PROTECTED) != 0) {
+            sb.append("|protected");
+        }
+        if ((flags & ACC_STATIC) != 0) {
+            sb.append("|static");
+        }
+        if ((flags & ACC_FINAL) != 0) {
+            sb.append("|final");
+        }
+        if ((flags & ACC_SYNCHRONIZED) != 0) {
+            if (what == CONV_CLASS) {
+                sb.append("|super");
+            } else {
+                sb.append("|synchronized");
+            }
+        }
+        if ((flags & ACC_VOLATILE) != 0) {
+            if (what == CONV_METHOD) {
+                sb.append("|bridge");
+            } else {
+                sb.append("|volatile");
+            }
+        }
+        if ((flags & ACC_TRANSIENT) != 0) {
+            if (what == CONV_METHOD) {
+                sb.append("|varargs");
+            } else {
+                sb.append("|transient");
+            }
+        }
+        if ((flags & ACC_NATIVE) != 0) {
+            sb.append("|native");
+        }
+        if ((flags & ACC_INTERFACE) != 0) {
+            sb.append("|interface");
+        }
+        if ((flags & ACC_ABSTRACT) != 0) {
+            sb.append("|abstract");
+        }
+        if ((flags & ACC_STRICT) != 0) {
+            sb.append("|strictfp");
+        }
+        if ((flags & ACC_SYNTHETIC) != 0) {
+            sb.append("|synthetic");
+        }
+        if ((flags & ACC_ANNOTATION) != 0) {
+            sb.append("|annotation");
+        }
+        if ((flags & ACC_ENUM) != 0) {
+            sb.append("|enum");
+        }
+        if ((flags & ACC_CONSTRUCTOR) != 0) {
+            sb.append("|constructor");
+        }
+        if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+            sb.append("|declared_synchronized");
+        }
+
+        if ((extra != 0) || (sb.length() == 0)) {
+            sb.append('|');
+            sb.append(Hex.u2(extra));
+        }
+
+        return sb.substring(1);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlock.java b/dx/src/com/android/dx/rop/code/BasicBlock.java
new file mode 100644
index 0000000..66db5aa
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlock.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Basic block of register-based instructions.
+ */
+public final class BasicBlock implements LabeledItem {
+    /** &gt;= 0; target label for this block */
+    private final int label;
+
+    /** non-null; list of instructions in this block */
+    private final InsnList insns;
+
+    /**
+     * non-null; full list of successors that this block may
+     * branch to 
+     */
+    private final IntList successors;
+
+    /**
+     * &gt;= -1; the primary / standard-flow / "default" successor, or
+     * <code>-1</code> if this block has no successors (that is, it
+     * exits the function/method) 
+     */
+    private final int primarySuccessor;
+
+    /**
+     * Constructs an instance. The predecessor set is set to <code>null</code>.
+     * 
+     * @param label &gt;= 0; target label for this block
+     * @param insns non-null; list of instructions in this block
+     * @param successors non-null; full list of successors that this
+     * block may branch to
+     * @param primarySuccessor &gt;= -1; the primary / standard-flow /
+     * "default" successor, or <code>-1</code> if this block has no
+     * successors (that is, it exits the function/method or is an
+     * unconditional throw)
+     */
+    public BasicBlock(int label, InsnList insns, IntList successors,
+                      int primarySuccessor) {
+        if (label < 0) {
+            throw new IllegalArgumentException("label < 0");
+        }
+
+        try {
+            insns.throwIfMutable();
+        } catch (NullPointerException ex) {
+            // Elucidate exception.
+            throw new NullPointerException("insns == null");
+        }
+
+        int sz = insns.size();
+
+        if (sz == 0) {
+            throw new IllegalArgumentException("insns.size() == 0");
+        }
+
+        for (int i = sz - 2; i >= 0; i--) {
+            Rop one = insns.get(i).getOpcode();
+            if (one.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new IllegalArgumentException("insns[" + i + "] is a " +
+                                                   "branch or can throw");
+            }
+        }
+
+        Insn lastInsn = insns.get(sz - 1);
+        if (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("insns does not end with " +
+                                               "a branch or throwing " +
+                                               "instruction");
+        }
+
+        try {
+            successors.throwIfMutable();
+        } catch (NullPointerException ex) {
+            // Elucidate exception.
+            throw new NullPointerException("successors == null");
+        }
+
+        if (primarySuccessor < -1) {
+            throw new IllegalArgumentException("primarySuccessor < -1");
+        }
+
+        if (primarySuccessor >= 0 && !successors.contains(primarySuccessor)) {
+            throw new IllegalArgumentException(
+                    "primarySuccessor not in successors");
+        }
+
+        this.label = label;
+        this.insns = insns;
+        this.successors = successors;
+        this.primarySuccessor = primarySuccessor;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Instances of this class compare by identity. That is,
+     * <code>x.equals(y)</code> is only true if <code>x == y</code>.
+     */
+    @Override
+    public boolean equals(Object other) {
+        return (this == other);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Return the identity hashcode of this instance. This is proper,
+     * since instances of this class compare by identity (see {@link #equals}).
+     */
+    @Override
+    public int hashCode() {
+        return System.identityHashCode(this);
+    }
+
+    /**
+     * Gets the target label of this block.
+     * 
+     * @return &gt;= 0; the label
+     */
+    public int getLabel() {
+        return label;
+    }
+
+    /**
+     * Gets the list of instructions inside this block.
+     * 
+     * @return non-null; the instruction list
+     */
+    public InsnList getInsns() {
+        return insns;
+    }
+
+    /**
+     * Gets the list of successors that this block may branch to.
+     * 
+     * @return non-null; the successors list
+     */
+    public IntList getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * Gets the primary successor of this block.
+     * 
+     * @return &gt;= -1; the primary successor, or <code>-1</code> if this
+     * block has no successors at all
+     */
+    public int getPrimarySuccessor() {
+        return primarySuccessor;
+    }
+
+    /**
+     * Gets the secondary successor of this block. It is only valid to call
+     * this method on blocks that have exactly two successors.
+     * 
+     * @return &gt;= 0; the secondary successor
+     */
+    public int getSecondarySuccessor() {
+        if (successors.size() != 2) {
+            throw new UnsupportedOperationException(
+                    "block doesn't have exactly two successors");
+        }
+
+        int succ = successors.get(0);
+        if (succ == primarySuccessor) {
+            succ = successors.get(1);
+        }
+
+        return succ;
+    }
+
+    /**
+     * Gets the first instruction of this block. This is just a
+     * convenient shorthand for <code>getInsns().get(0)</code>.
+     * 
+     * @return non-null; the first instruction
+     */
+    public Insn getFirstInsn() {
+        return insns.get(0);
+    }
+
+    /**
+     * Gets the last instruction of this block. This is just a
+     * convenient shorthand for <code>getInsns().getLast()</code>.
+     * 
+     * @return non-null; the last instruction
+     */
+    public Insn getLastInsn() {
+        return insns.getLast();
+    }
+
+    /**
+     * Returns whether this block might throw an exception. This is
+     * just a convenient shorthand for <code>getLastInsn().canThrow()</code>.
+     * 
+     * @return <code>true</code> iff this block might throw an
+     * exception
+     */
+    public boolean canThrow() {
+        return insns.getLast().canThrow();
+    }
+
+    /**
+     * Returns whether this block has any associated exception handlers.
+     * This is just a shorthand for inspecting the last instruction in
+     * the block to see if it could throw, and if so, whether it in fact
+     * has any associated handlers.
+     * 
+     * @return <code>true</code> iff this block has any associated
+     * exception handlers
+     */
+    public boolean hasExceptionHandlers() {
+        Insn lastInsn = insns.getLast();
+        return lastInsn.getCatches().size() != 0;
+    }
+
+    /**
+     * Returns the exception handler types associated with this block,
+     * if any. This is just a shorthand for inspecting the last
+     * instruction in the block to see if it could throw, and if so,
+     * grabbing the catch list out of it. If not, this returns an
+     * empty list (not <code>null</code>).
+     *
+     * @return non-null; the exception handler types associated with
+     * this block
+     */
+    public TypeList getExceptionHandlerTypes() {
+        Insn lastInsn = insns.getLast();
+        return lastInsn.getCatches();
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount.
+     * 
+     * @param delta the amount to offset register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public BasicBlock withRegisterOffset(int delta) {
+        return new BasicBlock(label, insns.withRegisterOffset(delta),
+                              successors, primarySuccessor);
+    }
+
+    public String toString() {
+        return '{' + Hex.u2(label) + '}';
+    }
+
+    /**
+     * BasicBlock visitor interface
+     */
+    public interface Visitor {
+        /**
+         * Visits a basic block
+         * @param b block visited
+         */
+        public void visitBlock (BasicBlock b);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlockList.java b/dx/src/com/android/dx/rop/code/BasicBlockList.java
new file mode 100644
index 0000000..6564318
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlockList.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link BasicBlock} instances.
+ */
+public final class BasicBlockList extends LabeledList {
+    /**
+     * &gt;= -1; the count of registers required by this method or
+     * <code>-1</code> if not yet calculated 
+     */
+    private int regCount;
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>,
+     * and the first-block label is initially <code>-1</code>.
+     * 
+     * @param size the size of the list
+     */
+    public BasicBlockList(int size) {
+        super(size);
+
+        regCount = -1;
+    }
+
+    /**
+     * Constructs a mutable copy for <code>getMutableCopy()</code>.
+     * 
+     * @param old block to copy
+     */
+    private BasicBlockList (BasicBlockList old) {
+        super(old);
+        regCount = old.regCount;
+    }
+
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public BasicBlock get(int n) {
+        return (BasicBlock) get0(n);
+    }
+
+    /**
+     * Sets the basic block at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param bb null-ok; the element to set at <code>n</code>
+     */
+    public void set(int n, BasicBlock bb) {
+        super.set(n, bb);
+        
+        // Reset regCount, since it will need to be recalculated.
+        regCount = -1;
+    }
+
+    /**
+     * Returns how many registers this method requires. This is simply
+     * the maximum of register-number-plus-category referred to by this
+     * instance's instructions (indirectly through {@link BasicBlock}
+     * instances).
+     * 
+     * @return &gt;= 0; the register count
+     */
+    public int getRegCount() {
+        if (regCount == -1) {
+            RegCountVisitor visitor = new RegCountVisitor();
+            forEachInsn(visitor);
+            regCount = visitor.getRegCount();
+        }
+
+        return regCount;
+    }
+
+    /**
+     * Gets the total instruction count for this instance. This is the
+     * sum of the instruction counts of each block.
+     * 
+     * @return &gt;= 0; the total instruction count
+     */
+    public int getInstructionCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) getOrNull0(i);
+            if (one != null) {
+                result += one.getInsns().size();
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the total instruction count for this instance, ignoring
+     * mark-local instructions which are not actually emitted.
+     *
+     * @return &gt;= 0; the total instruction count
+     */
+    public int getEffectiveInstructionCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) getOrNull0(i);
+            if (one != null) {
+                InsnList insns = one.getInsns();
+                int insnsSz = insns.size();
+
+                for (int j = 0; j < insnsSz; j++) {
+                    Insn insn = insns.get(j);
+
+                    if (insn.getOpcode().getOpcode() != RegOps.MARK_LOCAL) {
+                        result++;
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Gets the first block in the list with the given label, if any.
+     *
+     * @param label &gt;= 0; the label to look for
+     * @return non-null; the so-labelled block
+     * @throws IllegalArgumentException thrown if the label isn't found
+     */
+    public BasicBlock labelToBlock(int label) {
+        int idx = indexOfLabel(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label: "
+                    + Hex.u2(label));
+        }
+
+        return get(idx);
+    }
+
+    /**
+     * Visits each instruction of each block in the list, in order.
+     * 
+     * @param visitor non-null; visitor to use
+     */
+    public void forEachInsn(Insn.Visitor visitor) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = get(i);
+            InsnList insns = one.getInsns();
+            insns.forEach(visitor);
+        }
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount. Mutability of the result is inherited from the
+     * original.
+     * 
+     * @param delta the amount to offset register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public BasicBlockList withRegisterOffset(int delta) {
+        int sz = size();
+        BasicBlockList result = new BasicBlockList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) get0(i);
+            if (one != null) {
+                result.set(i, one.withRegisterOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a mutable copy of this list.
+     *
+     * @return non-null; an appropriately-constructed instance
+     */
+    public BasicBlockList getMutableCopy() {
+        return new BasicBlockList(this);
+    }
+
+    /**
+     * Gets the preferred successor for the given block. If the block
+     * only has one successor, then that is the preferred successor.
+     * Otherwise, if the block has a primay successor, then that is
+     * the preferred successor. If the block has no successors, then
+     * this returns <code>null</code>.
+     * 
+     * @param block non-null; the block in question
+     * @return null-ok; the preferred successor, if any
+     */
+    public BasicBlock preferredSuccessorOf(BasicBlock block) {
+        int primarySuccessor = block.getPrimarySuccessor();
+        IntList successors = block.getSuccessors();
+        int succSize = successors.size();
+
+        switch (succSize) {
+            case 0: {
+                return null;
+            }
+            case 1: {
+                return labelToBlock(successors.get(0));
+            }
+        }
+
+        if (primarySuccessor != -1) {
+            return labelToBlock(primarySuccessor);
+        } else {
+            return labelToBlock(successors.get(0));
+        }
+    }
+
+    /**
+     * Compares the catches of two blocks for equality. This includes
+     * both the catch types and target labels.
+     * 
+     * @param block1 non-null; one block to compare
+     * @param block2 non-null; the other block to compare
+     * @return <code>true</code> if the two blocks' non-primary successors
+     * are identical
+     */
+    public boolean catchesEqual(BasicBlock block1,
+            BasicBlock block2) {
+        TypeList catches1 = block1.getExceptionHandlerTypes();
+        TypeList catches2 = block2.getExceptionHandlerTypes();
+
+        if (!StdTypeList.equalContents(catches1, catches2)) {
+            return false;
+        }
+
+        IntList succ1 = block1.getSuccessors();
+        IntList succ2 = block2.getSuccessors();
+        int size = succ1.size(); // Both are guaranteed to be the same size.
+
+        int primary1 = block1.getPrimarySuccessor();
+        int primary2 = block2.getPrimarySuccessor();
+
+        if (((primary1 == -1) || (primary2 == -1))
+                && (primary1 != primary2)) {
+            /*
+             * For the current purpose, both blocks in question must
+             * either both have a primary or both not have a primary to
+             * be considered equal, and it turns out here that that's not
+             * the case.
+             */
+            return false;
+        }
+            
+        for (int i = 0; i < size; i++) {
+            int label1 = succ1.get(i);
+            int label2 = succ2.get(i);
+
+            if (label1 == primary1) {
+                /*
+                 * It should be the case that block2's primary is at the
+                 * same index. If not, we consider the blocks unequal for
+                 * the current purpose.
+                 */
+                if (label2 != primary2) {
+                    return false;
+                }
+                continue;
+            }
+
+            if (label1 != label2) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Instruction visitor class for counting registers used.
+     */
+    private static class RegCountVisitor
+            implements Insn.Visitor {
+        /** &gt;= 0; register count in-progress */
+        private int regCount;
+
+        /**
+         * Constructs an instance.
+         */
+        public RegCountVisitor() {
+            regCount = 0;
+        }
+
+        /**
+         * Gets the register count.
+         * 
+         * @return &gt;= 0; the count
+         */
+        public int getRegCount() {
+            return regCount;
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            visit(insn);
+        }
+
+        /**
+         * Helper for all the <code>visit*</code> methods.
+         * 
+         * @param insn non-null; instruction being visited
+         */
+        private void visit(Insn insn) {
+            RegisterSpec result = insn.getResult();
+
+            if (result != null) {
+                processReg(result);
+            }
+
+            RegisterSpecList sources = insn.getSources();
+            int sz = sources.size();
+
+            for (int i = 0; i < sz; i++) {
+                processReg(sources.get(i));
+            }
+        }
+
+        /**
+         * Processes the given register spec.
+         * 
+         * @param spec non-null; the register spec
+         */
+        private void processReg(RegisterSpec spec) {
+            int reg = spec.getNextReg();
+
+            if (reg > regCount) {
+                regCount = reg;
+            }
+        }
+    }    
+}
diff --git a/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
new file mode 100644
index 0000000..a9da109
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Implementation of {@link TranslationAdvice} which conservatively answers
+ * <code>false</code> to all methods.
+ */
+public final class ConservativeTranslationAdvice
+        implements TranslationAdvice {
+    /** non-null; standard instance of this class */
+    public static final ConservativeTranslationAdvice THE_ONE =
+        new ConservativeTranslationAdvice();
+
+    /**
+     * This class is not publicly instantiable. Use {@link #THE_ONE}.
+     */
+    private ConservativeTranslationAdvice() {
+        // This space intentionally left blank.
+    }
+    
+    /** {@inheritDoc} */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public boolean requiresSourcesInOrder(Rop opcode,
+            RegisterSpecList sources) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public int getMaxOptimalRegisterCount() {
+        return Integer.MAX_VALUE;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/CstInsn.java b/dx/src/com/android/dx/rop/code/CstInsn.java
new file mode 100644
index 0000000..d1cf523
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/CstInsn.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which contains an explicit reference to a constant.
+ */
+public abstract class CstInsn
+        extends Insn {
+    /** non-null; the constant */
+    private final Constant cst;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param sources non-null; specs for all the sources
+     * @param cst non-null; constant
+     */
+    public CstInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                   RegisterSpecList sources, Constant cst) {
+        super(opcode, position, result, sources);
+
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        this.cst = cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return cst.toHuman();
+    }
+
+    /**
+     * Gets the constant.
+     * 
+     * @return non-null; the constant
+     */
+    public Constant getConstant() {
+        return cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean contentEquals(Insn b) {
+        /*
+         * The cast (CstInsn)b below should always succeed since
+         * Insn.contentEquals compares classes of this and b.
+         */
+        return super.contentEquals(b)
+                && cst.equals(((CstInsn)b).getConstant());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
new file mode 100644
index 0000000..1c23824
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Implementation of {@link TranslationAdvice} which represents what
+ * the dex format will be able to represent.
+ */
+public final class DexTranslationAdvice
+        implements TranslationAdvice {
+    /** non-null; standard instance of this class */
+    public static final DexTranslationAdvice THE_ONE =
+        new DexTranslationAdvice();
+
+    /** debug advice for disabling invoke-range optimization */
+    public static final DexTranslationAdvice NO_SOURCES_IN_ORDER =
+        new DexTranslationAdvice(true);
+
+    /**
+     * The minimum source width, in register units, for an invoke
+     * instruction that requires its sources to be in order and contiguous.
+     */
+    private static final int MIN_INVOKE_IN_ORDER = 6;
+
+    /** when true: always returns false for requiresSourcesInOrder */
+    private final boolean disableSourcesInOrder;
+
+    /**
+     * This class is not publicly instantiable. Use {@link #THE_ONE}.
+     */
+    private DexTranslationAdvice() {
+        disableSourcesInOrder = false;
+    }
+
+    private DexTranslationAdvice(boolean disableInvokeRange) {
+        this.disableSourcesInOrder = disableInvokeRange;
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB) {
+        if (sourceA.getType() != Type.INT) {
+            return false;
+        }
+
+        if (! (sourceB.getTypeBearer() instanceof CstInteger)) {
+            return false;
+        }
+
+        CstInteger cst = (CstInteger) sourceB.getTypeBearer();
+
+        // TODO handle rsub
+        switch (opcode.getOpcode()) {
+            // These have 8 and 16 bit cst representations
+            case RegOps.REM:
+            case RegOps.ADD:
+            case RegOps.MUL:
+            case RegOps.DIV:
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+                return cst.fitsIn16Bits();
+            // These only have 8 bit cst reps
+            case RegOps.SHL:
+            case RegOps.SHR:
+            case RegOps.USHR:
+                return cst.fitsIn8Bits();
+            default:
+                return false;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean requiresSourcesInOrder(Rop opcode,
+            RegisterSpecList sources) {
+
+        return !disableSourcesInOrder && opcode.isCallLike()
+                && totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER;
+    }
+
+    /**
+     * Calculates the total rop width of the list of SSA registers
+     *
+     * @param sources non-null; list of SSA registers
+     * @return &gt;= 0 rop-form width in register units
+     */
+    private int totalRopWidth(RegisterSpecList sources) {
+        int sz = sources.size();
+        int total = 0;
+
+        for (int i = 0; i < sz; i++) {
+            total += sources.get(i).getCategory();
+        }
+
+        return total;
+    }
+
+    /** {@inheritDoc} */
+    public int getMaxOptimalRegisterCount() {
+        return 16;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Exceptions.java b/dx/src/com/android/dx/rop/code/Exceptions.java
new file mode 100644
index 0000000..3ef4879
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Exceptions.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Common exception types.
+ */
+public final class Exceptions {
+    /** non-null; the type <code>java.lang.ArithmeticException</code> */
+    public static final Type TYPE_ArithmeticException =
+        Type.intern("Ljava/lang/ArithmeticException;");
+
+    /**
+     * non-null; the type
+     * <code>java.lang.ArrayIndexOutOfBoundsException</code> 
+     */
+    public static final Type TYPE_ArrayIndexOutOfBoundsException =
+        Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;");
+
+    /** non-null; the type <code>java.lang.ArrayStoreException</code> */
+    public static final Type TYPE_ArrayStoreException =
+        Type.intern("Ljava/lang/ArrayStoreException;");
+
+    /** non-null; the type <code>java.lang.ClassCastException</code> */
+    public static final Type TYPE_ClassCastException =
+        Type.intern("Ljava/lang/ClassCastException;");
+
+    /** non-null; the type <code>java.lang.Error</code> */
+    public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;");
+
+    /**
+     * non-null; the type
+     * <code>java.lang.IllegalMonitorStateException</code> 
+     */
+    public static final Type TYPE_IllegalMonitorStateException =
+        Type.intern("Ljava/lang/IllegalMonitorStateException;");
+
+    /** non-null; the type <code>java.lang.NegativeArraySizeException</code> */
+    public static final Type TYPE_NegativeArraySizeException =
+        Type.intern("Ljava/lang/NegativeArraySizeException;");
+
+    /** non-null; the type <code>java.lang.NullPointerException</code> */
+    public static final Type TYPE_NullPointerException =
+        Type.intern("Ljava/lang/NullPointerException;");
+
+    /** non-null; the list <code>[java.lang.Error]</code> */
+    public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.ArithmeticException]</code> 
+     */
+    public static final StdTypeList LIST_Error_ArithmeticException =
+        StdTypeList.make(TYPE_Error, TYPE_ArithmeticException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.ClassCastException]</code> 
+     */
+    public static final StdTypeList LIST_Error_ClassCastException =
+        StdTypeList.make(TYPE_Error, TYPE_ClassCastException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.NegativeArraySizeException]</code> 
+     */
+    public static final StdTypeList LIST_Error_NegativeArraySizeException =
+        StdTypeList.make(TYPE_Error, TYPE_NegativeArraySizeException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.NullPointerException]</code> 
+     */
+    public static final StdTypeList LIST_Error_NullPointerException =
+        StdTypeList.make(TYPE_Error, TYPE_NullPointerException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.ArrayIndexOutOfBoundsException]</code> 
+     */
+    public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_ArrayIndexOutOfBoundsException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.ArrayIndexOutOfBoundsException,
+     * java.lang.ArrayStoreException]</code> 
+     */
+    public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_ArrayIndexOutOfBoundsException,
+                      TYPE_ArrayStoreException);
+
+    /**
+     * non-null; the list <code>[java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.IllegalMonitorStateException]</code> 
+     */
+    public static final StdTypeList
+        LIST_Error_Null_IllegalMonitorStateException =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_IllegalMonitorStateException);
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Exceptions() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
new file mode 100644
index 0000000..3798afb
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.StdTypeList;
+
+import java.util.ArrayList;
+
+/**
+ * Instruction which fills a newly created array with a predefined list of
+ * constant values.
+ */
+public final class FillArrayDataInsn
+        extends Insn {
+
+    /** non-null: initial values to fill the newly created array */
+    private final ArrayList<Constant> initValues;
+
+    /**
+     * non-null: type of the array. Will be used to determine the width of
+     * elements in the array-data table.
+     */
+    private final Constant arrayType;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param sources non-null; specs for all the sources
+     * @param initValues non-null; list of initial values to fill the array
+     * @param cst non-null; type of the new array
+     */
+    public FillArrayDataInsn(Rop opcode, SourcePosition position,
+                             RegisterSpecList sources,
+                             ArrayList<Constant> initValues,
+                             Constant cst) {
+        super(opcode, position, null, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        this.initValues = initValues;
+        this.arrayType = cst;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /**
+     * Return the list of init values
+     * @return non-null; list of init values
+     */
+    public ArrayList<Constant> getInitValues() {
+        return initValues;
+    }
+
+    /**
+     * Return the type of the newly created array
+     * @return non-null; array type
+     */
+    public Constant getConstant() {
+        return arrayType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitFillArrayDataInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new  UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new FillArrayDataInsn(getOpcode(), getPosition(),
+                                     getSources().withOffset(delta),
+                                     initValues, arrayType);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new FillArrayDataInsn(getOpcode(), getPosition(),
+                                     sources, initValues, arrayType);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Insn.java b/dx/src/com/android/dx/rop/code/Insn.java
new file mode 100644
index 0000000..b1ad0ad
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Insn.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * A register-based instruction. An instruction is the combination of
+ * an opcode (which specifies operation and source/result types), a
+ * list of actual sources and result registers/values, and additional
+ * information.
+ */
+public abstract class Insn implements ToHuman {
+    /** non-null; opcode */
+    private final Rop opcode;
+
+    /** non-null; source position */
+    private final SourcePosition position;
+
+    /** null-ok; spec for the result of this instruction, if any */
+    private final RegisterSpec result;
+
+    /** non-null; specs for all the sources of this instruction */
+    private final RegisterSpecList sources;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param sources non-null; specs for all the sources
+     */
+    public Insn(Rop opcode, SourcePosition position, RegisterSpec result,
+                RegisterSpecList sources) {
+        if (opcode == null) {
+            throw new NullPointerException("opcode == null");
+        }
+
+        if (position == null) {
+            throw new NullPointerException("position == null");
+        }
+
+        if (sources == null) {
+            throw new NullPointerException("sources == null");
+        }
+
+        this.opcode = opcode;
+        this.position = position;
+        this.result = result;
+        this.sources = sources;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Instances of this class compare by identity. That is,
+     * <code>x.equals(y)</code> is only true if <code>x == y</code>.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        return (this == other);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * This implementation returns the identity hashcode of this
+     * instance. This is proper, since instances of this class compare
+     * by identity (see {@link #equals}).
+     */
+    @Override
+    public final int hashCode() {
+        return System.identityHashCode(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toStringWithInline(getInlineString());
+    }
+
+    /**
+     * Gets a human-oriented (and slightly lossy) string for this instance.
+     * 
+     * @return non-null; the human string form
+     */
+    public String toHuman() {
+        return toHumanWithInline(getInlineString());
+    }
+
+    /**
+     * Gets an "inline" string portion for toHuman(), if available. This
+     * is the portion that appears after the Rop opcode
+     * 
+     * @return null-ok; if non-null, the inline text for toHuman()
+     */
+    public String getInlineString() {
+        return null;
+    }
+
+    /**
+     * Gets the opcode.
+     * 
+     * @return non-null; the opcode
+     */
+    public final Rop getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the source position.
+     * 
+     * @return non-null; the source position
+     */
+    public final SourcePosition getPosition() {
+        return position;
+    }
+
+    /**
+     * Gets the result spec, if any. A return value of <code>null</code>
+     * means this instruction returns nothing.
+     * 
+     * @return null-ok; the result spec, if any
+     */
+    public final RegisterSpec getResult() {
+        return result;
+    }
+
+    /**
+     * Gets the spec of a local variable assignment that occurs at this
+     * instruction, or null if no local variable assignment occurs. This
+     * may be the result register, or for <code>mark-local</code> insns
+     * it may be the source.
+     * 
+     * @return null-ok; a named register spec or null
+     */
+    public final RegisterSpec getLocalAssignment() {
+        RegisterSpec assignment;
+        if (opcode.getOpcode() == RegOps.MARK_LOCAL) {
+            assignment = sources.get(0);
+        } else {
+            assignment = result;
+        }
+
+        if (assignment == null) {
+            return null;
+        }
+
+        LocalItem localItem = assignment.getLocalItem();
+
+        if (localItem == null) {
+            return null;
+        }
+
+        return assignment;
+    }
+
+    /**
+     * Gets the source specs.
+     * 
+     * @return non-null; the source specs
+     */
+    public final RegisterSpecList getSources() {
+        return sources;
+    }
+
+    /**
+     * Gets whether this instruction can possibly throw an exception. This
+     * is just a convenient wrapper for <code>getOpcode().canThrow()</code>.
+     * 
+     * @return <code>true</code> iff this instruction can possibly throw
+     */
+    public final boolean canThrow() {
+        return opcode.canThrow();
+    }
+
+    /**
+     * Gets the list of possibly-caught exceptions. This returns {@link
+     * StdTypeList#EMPTY} if this instruction has no handlers,
+     * which can be <i>either</i> if this instruction can't possibly
+     * throw or if it merely doesn't handle any of its possible
+     * exceptions. To determine whether this instruction can throw,
+     * use {@link #canThrow}.
+     * 
+     * @return non-null; the catches list
+     */
+    public abstract TypeList getCatches();
+
+    /**
+     * Calls the appropriate method on the given visitor, depending on the
+     * class of this instance. Subclasses must override this.
+     * 
+     * @param visitor non-null; the visitor to call on
+     */
+    public abstract void accept(Visitor visitor);
+
+    /**
+     * Returns an instance that is just like this one, except that it
+     * has a catch list with the given item appended to the end. This
+     * method throws an exception if this instance can't possibly
+     * throw. To determine whether this instruction can throw, use
+     * {@link #canThrow}.
+     * 
+     * @param type non-null; type to append to the catch list
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract Insn withAddedCatch(Type type);
+
+    /**
+     * Returns an instance that is just like this one, except that all
+     * register references have been offset by the given delta.
+     * 
+     * @param delta the amount to offset register references by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract Insn withRegisterOffset(int delta);
+
+    /**
+     * Returns an instance that is just like this one, except that, if
+     * possible, the insn is converted into a version in which the last
+     * source (if it is a constant) is represented directly rather than
+     * as a register reference. <code>this</code> is returned in cases where
+     * the translation is not possible.
+     * 
+     * @return non-null; an appropriately-constructed instance
+     */
+    public Insn withLastSourceLiteral() {
+        return this;
+    }
+
+    /**
+     * Returns an exact copy of this Insn
+     *
+     * @return non-null; an appropriately-constructed instance
+     */
+    public Insn copy() {
+        return withRegisterOffset(0);
+    }
+
+
+    /**
+     * Compares, handling nulls safely
+     *
+     * @param a first object
+     * @param b second object
+     * @return true if they're equal or both null.
+     */
+    private static boolean equalsHandleNulls (Object a, Object b) {
+        return (a == b) || ((a != null) && a.equals(b));
+    }
+
+    /**
+     * Compares Insn contents, since <code>Insn.equals()</code> is defined
+     * to be an identity compare. Insn's are <code>contentEquals()</code>
+     * if they have the same opcode, registers, source position, and other
+     * metadata.
+     * 
+     * @return true in the case described above
+     */
+    public boolean contentEquals(Insn b) {
+        return opcode == b.getOpcode()
+                && position.equals(b.getPosition())
+                && (getClass() == b.getClass())
+                && equalsHandleNulls(result, b.getResult())
+                && equalsHandleNulls(sources, b.getSources())
+                && StdTypeList.equalContents(getCatches(), b.getCatches());
+    }
+
+    /**
+     * Returns an instance that is just like this one, except
+     * with new result and source registers.
+     *
+     * @param result null-ok; new result register
+     * @param sources non-null; new sources registers
+     * @return non-null; an appropriately-constructed instance
+     */
+    public abstract Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources);
+
+    /**
+     * Returns the string form of this instance, with the given bit added in
+     * the standard location for an inline argument.
+     * 
+     * @param extra null-ok; the inline argument string
+     * @return non-null; the string form
+     */
+    protected final String toStringWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append("Insn{");
+        sb.append(position);
+        sb.append(' ');
+        sb.append(opcode);
+
+        if (extra != null) {
+            sb.append(' ');
+            sb.append(extra);
+        }
+
+        sb.append(" :: ");
+
+        if (result != null) {
+            sb.append(result);
+            sb.append(" <- ");
+        }
+
+        sb.append(sources);
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the human string form of this instance, with the given
+     * bit added in the standard location for an inline argument.
+     * 
+     * @param extra null-ok; the inline argument string
+     * @return non-null; the human string form
+     */
+    protected final String toHumanWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append(position);
+        sb.append(": ");
+        sb.append(opcode.getNickname());
+
+        if (extra != null) {
+            sb.append("(");
+            sb.append(extra);
+            sb.append(")");
+        }
+
+        if (result == null) {
+            sb.append(" .");
+        } else {
+            sb.append(" ");
+            sb.append(result.toHuman());
+        }
+
+        sb.append(" <-");
+
+        int sz = sources.size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(" ");
+                sb.append(sources.get(i).toHuman());
+            }
+        }
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Visitor interface for this (outer) class.
+     */
+    public static interface Visitor {
+        /**
+         * Visits a {@link PlainInsn}.
+         * 
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitPlainInsn(PlainInsn insn);
+
+        /**
+         * Visits a {@link PlainCstInsn}.
+         * 
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitPlainCstInsn(PlainCstInsn insn);
+
+        /**
+         * Visits a {@link SwitchInsn}.
+         * 
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitSwitchInsn(SwitchInsn insn);
+
+        /**
+         * Visits a {@link ThrowingCstInsn}.
+         * 
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn);
+
+        /**
+         * Visits a {@link ThrowingInsn}.
+         * 
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitThrowingInsn(ThrowingInsn insn);
+
+        /**
+         * Visits a {@link FillArrayDataInsn}.
+         *
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn);
+    }
+
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    public static class BaseVisitor implements Visitor {
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            // This space intentionally left blank.
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/InsnList.java b/dx/src/com/android/dx/rop/code/InsnList.java
new file mode 100644
index 0000000..34f124c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/InsnList.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Insn} instances.
+ */
+public final class InsnList
+        extends FixedSizeList {
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     *
+     * @param size the size of the list
+     */
+    public InsnList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw <code>NullPointerException</code>.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @return non-null; element at that index
+     */
+    public Insn get(int n) {
+        return (Insn) get0(n);
+    }
+
+    /**
+     * Sets the instruction at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which index
+     * @param insn non-null; the instruction to set at <code>n</code>
+     */
+    public void set(int n, Insn insn) {
+        set0(n, insn);
+    }
+
+    /**
+     * Gets the last instruction. This is just a convenient shorthand for
+     * <code>get(size() - 1)</code>.
+     * 
+     * @return non-null; the last instruction
+     */
+    public Insn getLast() {
+        return get(size() - 1);
+    }
+
+    /**
+     * Visits each instruction in the list, in order.
+     *
+     * @param visitor non-null; visitor to use
+     */
+    public void forEach(Insn.Visitor visitor) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            get(i).accept(visitor);
+        }
+    }
+
+    /**
+     * Compares the contents of this <code>InsnList</code> with another.
+     * The blocks must have the same number of insns, and each Insn must
+     * also return true to <code>Insn.contentEquals()</code>.
+     *
+     * @param b to compare
+     * @return true in the case described above.
+     */
+    public boolean contentEquals(InsnList b) {
+        if (b == null) return false;
+
+        int sz = size();
+
+        if (sz != b.size()) return false;
+        
+        for (int i = 0; i < sz; i++) {
+            if (!get(i).contentEquals(b.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount. Mutability of the result is inherited from the
+     * original.
+     * 
+     * @param delta the amount to offset register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public InsnList withRegisterOffset(int delta) {
+        int sz = size();
+        InsnList result = new InsnList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            Insn one = (Insn) get0(i);
+            if (one != null) {
+                result.set0(i, one.withRegisterOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java
new file mode 100644
index 0000000..b1e1a4b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalItem.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A local variable item: either a name or a signature or both.
+ */
+public class LocalItem implements Comparable<LocalItem> {
+
+    /** null-ok; local variable name */
+    private final CstUtf8 name;
+
+    /** null-ok; local variable signature */
+    private final CstUtf8 signature;
+
+    /**
+     * Make a new item. If both name and signature are null, null is returned.
+     *
+     * TODO: intern these
+     *
+     * @param name null-ok; local variable name
+     * @param signature null-ok; local variable signature
+     * @return non-null; appropriate instance.
+     */
+    public static LocalItem make (CstUtf8 name, CstUtf8 signature) {
+        if (name == null && signature == null) {
+            return null;
+        }
+
+        return new LocalItem (name, signature);
+    }
+
+    /**
+     * Constructs instance.
+     *
+     * @param name null-ok; local variable name
+     * @param signature null-ok; local variable signature
+     */
+    private LocalItem (CstUtf8 name, CstUtf8 signature) {
+        this.name = name;
+        this.signature = signature;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals (Object other) {
+        if (!(other instanceof LocalItem)) {
+            return false;
+        }
+
+        LocalItem local = (LocalItem) other;
+
+        return 0 == compareTo(local);
+    }
+
+    /** 
+     * Compares two strings like String.compareTo(), excepts treats a null
+     * as the least-possible string value.
+     *
+     * @return negative integer, zero, or positive integer in accordance
+     * with Comparable.compareTo()
+     */
+    private static int compareHandlesNulls(CstUtf8 a, CstUtf8 b) {
+        if (a == b) {
+            return 0;
+        } else if (a == null) {
+            return -1;
+        } else if (b == null) {
+            return 1;
+        } else {
+            return a.compareTo(b);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo (LocalItem local) {
+        int ret;
+
+        ret = compareHandlesNulls(name, local.name);
+
+        if (ret != 0) {
+            return ret;
+        }
+
+        ret = compareHandlesNulls(signature, local.signature);
+
+        return ret;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode () {
+        return (name == null ? 0 : name.hashCode()) * 31
+                + (signature == null ? 0 : signature.hashCode());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        if (name != null && signature == null) {
+            return name.toQuoted();
+        } else if (name == null && signature == null) {
+            return "";
+        }
+
+        return "[" + (name == null ? "" : name.toQuoted())
+                + "|" + (signature == null ? "" : signature.toQuoted());        
+    }
+
+    /**
+     * Gets name.
+     *
+     * @return null-ok; name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+
+    /**
+     * Gets signature.
+     *
+     * @return null-ok; signature
+     */
+    public CstUtf8 getSignature() {
+        return signature;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
new file mode 100644
index 0000000..2d4cbce
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method.
+ */
+public final class LocalVariableExtractor {
+    /** non-null; method being extracted from */
+    private final RopMethod method;
+
+    /** non-null; block list for the method */
+    private final BasicBlockList blocks;
+
+    /** non-null; result in-progress */
+    private final LocalVariableInfo resultInfo;
+
+    /** non-null; work set indicating blocks needing to be processed */
+    private final int[] workSet;
+
+    /**
+     * Extracts out all the local variable information from the given method.
+     * 
+     * @param method non-null; the method to extract from
+     * @return non-null; the extracted information
+     */
+    public static LocalVariableInfo extract(RopMethod method) {
+        LocalVariableExtractor lve = new LocalVariableExtractor(method);
+        return lve.doit();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #extract}.
+     * 
+     * @param method non-null; the method to extract from
+     */
+    private LocalVariableExtractor(RopMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.method = method;
+        this.blocks = blocks;
+        this.resultInfo = new LocalVariableInfo(method);
+        this.workSet = Bits.makeBitSet(maxLabel);
+    }
+
+    /**
+     * Does the extraction.
+     * 
+     * @return non-null; the extracted information
+     */
+    private LocalVariableInfo doit() {
+        for (int label = method.getFirstLabel();
+             label >= 0;
+             label = Bits.findFirst(workSet, 0)) {
+            Bits.clear(workSet, label);
+            processBlock(label);
+        }
+        
+        resultInfo.setImmutable();
+        return resultInfo;
+    }
+
+    /**
+     * Processes a single block.
+     * 
+     * @param label &gt;= 0; label of the block to process
+     */
+    private void processBlock(int label) {
+        RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label);
+        BasicBlock block = blocks.labelToBlock(label);
+        InsnList insns = block.getInsns();
+        int insnSz = insns.size();
+
+        /*
+         * We may have to treat the last instruction specially: If it
+         * can (but doesn't always) throw, and the exception can be
+         * caught within the same method, then we need to use the
+         * state *before* executing it to be what is merged into
+         * exception targets.
+         */
+        Insn lastInsn = insns.getLast();
+        boolean canThrowDuringLastInsn = block.hasExceptionHandlers() &&
+            (insns.getLast().getResult() != null);
+        int freezeSecondaryStateAt = insnSz - 1;
+        RegisterSpecSet secondaryState = primaryState;
+
+        /*
+         * Iterate over the instructions, adding information for each place
+         * that the active variable set changes.
+         */
+
+        for (int i = 0; i < insnSz; i++) {
+            if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+                // Until this point, primaryState == secondaryState.
+                primaryState.setImmutable();
+                primaryState = primaryState.mutableCopy();
+            }
+
+            Insn insn = insns.get(i);
+            RegisterSpec result;
+
+            result = insn.getLocalAssignment();
+
+            if (result == null) {
+                /*
+                 * If an assignment assigns over an existing local, make
+                 * sure to mark the local as going out of scope.
+                 */
+
+                result = insn.getResult();
+
+                if (result != null
+                        && primaryState.get(result.getReg()) != null) {
+                    primaryState.remove(primaryState.get(result.getReg()));
+                }
+                continue;
+            }
+
+            result = result.withSimpleType();
+
+            RegisterSpec already = primaryState.get(result);
+            /*
+             * The equals() check ensures we only add new info if
+             * the instruction causes a change to the set of
+             * active variables.
+             */
+            if (!result.equals(already)) {
+                /*
+                 * If this insn represents a local moving from one register
+                 * to another, remove the association between the old register
+                 * and the local.
+                 */
+                RegisterSpec previous
+                        = primaryState.localItemToSpec(result.getLocalItem());
+
+                if (previous != null
+                        && (previous.getReg() != result.getReg())) {
+
+                    primaryState.remove(previous);                    
+                }
+
+                resultInfo.addAssignment(insn, result);
+                primaryState.put(result);
+            }
+        }
+
+        primaryState.setImmutable();
+
+        /*
+         * Merge this state into the start state for each successor,
+         * and update the work set where required (that is, in cases
+         * where the start state for a block changes).
+         */
+
+        IntList successors = block.getSuccessors();
+        int succSz = successors.size();
+        int primarySuccessor = block.getPrimarySuccessor();
+
+        for (int i = 0; i < succSz; i++) {
+            int succ = successors.get(i);
+            RegisterSpecSet state = (succ == primarySuccessor) ?
+                primaryState : secondaryState;
+
+            if (resultInfo.mergeStarts(succ, state)) {
+                Bits.set(workSet, succ);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableInfo.java b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
new file mode 100644
index 0000000..29c239b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.HashMap;
+
+/**
+ * Container for local variable information for a particular {@link
+ * RopMethod}.
+ */
+public final class LocalVariableInfo
+        extends MutabilityControl {
+    /** &gt;= 0; the register count for the method */
+    private final int regCount;
+
+    /**
+     * non-null; {@link RegisterSpecSet} to use when indicating a block
+     * that has no locals; it is empty and immutable but has an appropriate
+     * max size for the method 
+     */
+    private final RegisterSpecSet emptySet;
+
+    /**
+     * non-null; array consisting of register sets representing the
+     * sets of variables already assigned upon entry to each block,
+     * where array indices correspond to block labels 
+     */
+    private final RegisterSpecSet[] blockStarts;
+
+    /** non-null; map from instructions to the variable each assigns */
+    private final HashMap<Insn, RegisterSpec> insnAssignments;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param method non-null; the method being represented by this instance
+     */
+    public LocalVariableInfo(RopMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.regCount = blocks.getRegCount();
+        this.emptySet = new RegisterSpecSet(regCount);
+        this.blockStarts = new RegisterSpecSet[maxLabel];
+        this.insnAssignments =
+            new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
+
+        emptySet.setImmutable();
+    }
+
+    /**
+     * Sets the register set associated with the start of the block with
+     * the given label.
+     * 
+     * @param label &gt;= 0; the block label
+     * @param specs non-null; the register set to associate with the block
+     */
+    public void setStarts(int label, RegisterSpecSet specs) {
+        throwIfImmutable();
+
+        if (specs == null) {
+            throw new NullPointerException("specs == null");
+        }
+
+        try {
+            blockStarts[label] = specs;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus label");
+        }
+    }
+
+    /**
+     * Merges the given register set into the set for the block with the
+     * given label. If there was not already an associated set, then this
+     * is the same as calling {@link #setStarts}. Otherwise, this will
+     * merge the two sets and call {@link #setStarts} on the result of the
+     * merge.
+     * 
+     * @param label &gt;= 0; the block label
+     * @param specs non-null; the register set to merge into the start set
+     * for the block
+     * @return <code>true</code> if the merge resulted in an actual change
+     * to the associated set (including storing one for the first time) or
+     * <code>false</code> if there was no change
+     */
+    public boolean mergeStarts(int label, RegisterSpecSet specs) {
+        RegisterSpecSet start = getStarts0(label);
+        boolean changed = false;
+
+        if (start == null) {
+            setStarts(label, specs);
+            return true;
+        }
+
+        RegisterSpecSet newStart = start.mutableCopy();
+        newStart.intersect(specs, true);
+
+        if (start.equals(newStart)) {
+            return false;
+        }
+
+        newStart.setImmutable();
+        setStarts(label, newStart);
+
+        return true;
+    }
+
+    /**
+     * Gets the register set associated with the start of the block
+     * with the given label. This returns an empty set with the appropriate
+     * max size if no set was associated with the block in question.
+     * 
+     * @param label &gt;= 0; the block label
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet getStarts(int label) {
+        RegisterSpecSet result = getStarts0(label);
+
+        return (result != null) ? result : emptySet;
+    }
+
+    /**
+     * Gets the register set associated with the start of the given
+     * block. This is just convenient shorthand for
+     * <code>getStarts(block.getLabel())</code>.
+     * 
+     * @param block non-null; the block in question
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet getStarts(BasicBlock block) {
+        return getStarts(block.getLabel());
+    }
+
+    /**
+     * Gets a mutable copy of the register set associated with the
+     * start of the block with the given label. This returns a
+     * newly-allocated empty {@link RegisterSpecSet} of appropriate
+     * max size if there is not yet any set associated with the block.
+     * 
+     * @param label &gt;= 0; the block label
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet mutableCopyOfStarts(int label) {
+        RegisterSpecSet result = getStarts0(label);
+
+        return (result != null) ?
+            result.mutableCopy() : new RegisterSpecSet(regCount);
+    }
+
+    /**
+     * Adds an assignment association for the given instruction and
+     * register spec. This throws an exception if the instruction
+     * doesn't actually perform a named variable assignment.
+     * 
+     * <b>Note:</b> Although the instruction contains its own spec for
+     * the result, it still needs to be passed in explicitly to this
+     * method, since the spec that is stored here should always have a
+     * simple type and the one in the instruction can be an arbitrary
+     * {@link TypeBearer} (such as a constant value).
+     * 
+     * @param insn non-null; the instruction in question
+     * @param spec non-null; the associated register spec
+     */
+    public void addAssignment(Insn insn, RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (insn == null) {
+            throw new NullPointerException("insn == null");
+        }
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        insnAssignments.put(insn, spec);
+    }
+
+    /**
+     * Gets the named register being assigned by the given instruction, if
+     * previously stored in this instance.
+     * 
+     * @param insn non-null; instruction in question
+     * @return null-ok; the named register being assigned, if any
+     */
+    public RegisterSpec getAssignment(Insn insn) {
+        return insnAssignments.get(insn);
+    }
+
+    /**
+     * Gets the number of assignments recorded by this instance.
+     * 
+     * @return &gt;= 0; the number of assignments
+     */
+    public int getAssignmentCount() {
+        return insnAssignments.size();
+    }
+
+    public void debugDump() {
+        for (int label = 0 ; label < blockStarts.length; label++) {
+            if (blockStarts[label] == null) {
+                continue;
+            }
+
+            if (blockStarts[label] == emptySet) {
+                System.out.printf("%04x: empty set\n", label);
+            } else {
+                System.out.printf("%04x: %s\n", label, blockStarts[label]);
+            }
+        }
+    }
+
+    /**
+     * Helper method, to get the starts for a label, throwing the
+     * right exception for range problems.
+     * 
+     * @param label &gt;= 0; the block label
+     * @return null-ok; associated register set or <code>null</code> if there
+     * is none
+     */
+    private RegisterSpecSet getStarts0(int label) {
+        try {
+            return blockStarts[label];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus label");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainCstInsn.java b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
new file mode 100644
index 0000000..908b3cb
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * but which cannot throw an exception.
+ */
+public final class PlainCstInsn
+        extends CstInsn {
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param sources non-null; specs for all the sources
+     * @param cst non-null; the constant
+     */
+    public PlainCstInsn(Rop opcode, SourcePosition position,
+                        RegisterSpec result, RegisterSpecList sources,
+                        Constant cst) {
+        super(opcode, position, result, sources, cst);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitPlainCstInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new PlainCstInsn(getOpcode(), getPosition(),
+                                getResult().withOffset(delta),
+                                getSources().withOffset(delta),
+                                getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new PlainCstInsn(getOpcode(), getPosition(),
+                                result,
+                                sources,
+                                getConstant());
+
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainInsn.java b/dx/src/com/android/dx/rop/code/PlainInsn.java
new file mode 100644
index 0000000..4c5c9b7
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainInsn.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Plain instruction, which has no embedded data and which cannot possibly
+ * throw an exception.
+ */
+public final class PlainInsn
+        extends Insn {
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param sources non-null; specs for all the sources
+     */
+    public PlainInsn(Rop opcode, SourcePosition position,
+                     RegisterSpec result, RegisterSpecList sources) {
+        super(opcode, position, result, sources);
+
+        switch (opcode.getBranchingness()) {
+            case Rop.BRANCH_SWITCH:
+            case Rop.BRANCH_THROW: {
+                throw new IllegalArgumentException("bogus branchingness");
+            }
+        }
+
+        if (result != null && opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            // move-result-pseudo is required here
+            throw new IllegalArgumentException
+                    ("can't mix branchingness with result");            
+        }
+    }
+
+    /**
+     * Constructs a single-source instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param source non-null; spec for the source
+     */
+    public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                     RegisterSpec source) {
+        this(opcode, position, result, RegisterSpecList.make(source));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitPlainInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new PlainInsn(getOpcode(), getPosition(),
+                             getResult().withOffset(delta),
+                             getSources().withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withLastSourceLiteral() {
+        RegisterSpecList sources = getSources();
+        int szSources = sources.size();
+
+        if (szSources == 0) {
+            return this;
+        }
+
+        TypeBearer lastType = sources.get(szSources - 1).getTypeBearer();
+
+        if (!lastType.isConstant()) {
+            return this;
+        }
+
+        Constant cst = (Constant) lastType;
+
+        RegisterSpecList newSources = sources.withoutLast();
+
+        Rop newRop;
+        try {
+            newRop = Rops.ropFor(getOpcode().getOpcode(),
+                    getResult(), newSources, (Constant)lastType);
+        } catch (IllegalArgumentException ex) {
+            // There's no rop for this case
+            return this;
+        }
+
+        return new PlainCstInsn(newRop, getPosition(),
+                getResult(), newSources, cst);
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new PlainInsn(getOpcode(), getPosition(),
+                             result,
+                             sources);
+
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegOps.java b/dx/src/com/android/dx/rop/code/RegOps.java
new file mode 100644
index 0000000..f201f68
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegOps.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * All the register-based opcodes, and related utilities.
+ * 
+ * <p><b>Note:</b> Opcode descriptions use a rough pseudocode. <code>r</code>
+ * is the result register, <code>x</code> is the first argument,
+ * <code>y</code> is the second argument, and <code>z</code> is the
+ * third argument. The expression which describes
+ * the operation uses Java-ish syntax but is preceded by type indicators for
+ * each of the values.
+ */
+public final class RegOps {
+    /** <code>nop()</code> */
+    public static final int NOP = 1;
+
+    /** <code>T: any type; r,x: T :: r = x;</code> */
+    public static final int MOVE = 2;
+
+    /** <code>T: any type; r,param(x): T :: r = param(x)</code> */
+    public static final int MOVE_PARAM = 3;
+
+    /**
+     * <code>T: Throwable; r: T :: r = caught_exception</code>.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block, and such blocks must be
+     * the start of an exception handler.
+     */
+    public static final int MOVE_EXCEPTION = 4;
+
+    /** <code>T: any type; r, literal: T :: r = literal;</code> */
+    public static final int CONST = 5;
+
+    /** <code>goto <i>label</i></code> */
+    public static final int GOTO = 6;
+
+    /**
+     * <code>T: int or Object; x,y: T :: if (x == y) goto
+     * <i>label</i></code> 
+     */
+    public static final int IF_EQ = 7;
+
+    /**
+     * <code>T: int or Object; x,y: T :: if (x != y) goto
+     * <i>label</i></code> 
+     */
+    public static final int IF_NE = 8;
+
+    /** <code>x,y: int :: if (x &lt; y) goto <i>label</i></code> */
+    public static final int IF_LT = 9;
+
+    /** <code>x,y: int :: if (x &gt;= y) goto <i>label</i></code> */
+    public static final int IF_GE = 10;
+
+    /** <code>x,y: int :: if (x &lt;= y) goto <i>label</i></code> */
+    public static final int IF_LE = 11;
+
+    /** <code>x,y: int :: if (x &gt; y) goto <i>label</i></code> */
+    public static final int IF_GT = 12;
+
+    /** <code>x: int :: goto <i>table[x]</i></code> */
+    public static final int SWITCH = 13;
+
+    /** <code>T: any numeric type; r,x,y: T :: r = x + y</code> */
+    public static final int ADD = 14;
+
+    /** <code>T: any numeric type; r,x,y: T :: r = x - y</code> */
+    public static final int SUB = 15;
+
+    /** <code>T: any numeric type; r,x,y: T :: r = x * y</code> */
+    public static final int MUL = 16;
+
+    /** <code>T: any numeric type; r,x,y: T :: r = x / y</code> */
+    public static final int DIV = 17;
+
+    /**
+     * <code>T: any numeric type; r,x,y: T :: r = x % y</code>
+     * (Java-style remainder) 
+     */
+    public static final int REM = 18;
+
+    /** <code>T: any numeric type; r,x: T :: r = -x</code> */
+    public static final int NEG = 19;
+
+    /** <code>T: any integral type; r,x,y: T :: r = x &amp; y</code> */
+    public static final int AND = 20;
+
+    /** <code>T: any integral type; r,x,y: T :: r = x | y</code> */
+    public static final int OR = 21;
+
+    /** <code>T: any integral type; r,x,y: T :: r = x ^ y</code> */
+    public static final int XOR = 22;
+
+    /**
+     * <code>T: any integral type; r,x: T; y: int :: r = x &lt;&lt;
+     * y</code> 
+     */
+    public static final int SHL = 23;
+
+    /**
+     * <code>T: any integral type; r,x: T; y: int :: r = x &gt;&gt;
+     * y</code> (signed right-shift) 
+     */
+    public static final int SHR = 24;
+
+    /**
+     * <code>T: any integral type; r,x: T; y: int :: r = x
+     * &gt;&gt;&gt; y</code> (unsigned right-shift) 
+     */
+    public static final int USHR = 25;
+
+    /** <code>T: any integral type; r,x: T :: r = ~x</code> */
+    public static final int NOT = 26;
+
+    /**
+     * <code>T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0
+     * : (x &gt; y) ? 1 : -1</code> (Java-style "cmpl" where a NaN is
+     * considered "less than" all other values; also used for integral
+     * comparisons) 
+     */
+    public static final int CMPL = 27;
+
+    /**
+     * <code>T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0
+     * : (x &lt; y) ? -1 : 1</code> (Java-style "cmpg" where a NaN is
+     * considered "greater than" all other values) 
+     */
+    public static final int CMPG = 28;
+
+    /**
+     * <code>T: any numeric type; U: any numeric type; r: T; x: U ::
+     * r = (T) x</code> (numeric type conversion between the four
+     * "real" numeric types) 
+     */
+    public static final int CONV = 29;
+
+    /**
+     * <code>r,x: int :: r = (x &lt;&lt; 24) &gt;&gt; 24</code> (Java-style
+     * convert int to byte) 
+     */
+    public static final int TO_BYTE = 30;
+
+    /**
+     * <code>r,x: int :: r = x &amp; 0xffff</code> (Java-style
+     * convert int to char) 
+     */
+    public static final int TO_CHAR = 31;
+
+    /**
+     * <code>r,x: int :: r = (x &lt;&lt; 16) &gt;&gt; 16</code> (Java-style
+     * convert int to short) 
+     */
+    public static final int TO_SHORT = 32;
+
+    /** <code>T: return type for the method; x: T; return x</code> */
+    public static final int RETURN = 33;
+
+    /** <code>T: any type; r: int; x: T[]; :: r = x.length</code> */
+    public static final int ARRAY_LENGTH = 34;
+
+    /** <code>x: Throwable :: throw(x)</code> */
+    public static final int THROW = 35;
+
+    /** <code>x: Object :: monitorenter(x)</code> */
+    public static final int MONITOR_ENTER = 36;
+
+    /** <code>x: Object :: monitorexit(x)</code> */
+    public static final int MONITOR_EXIT = 37;
+
+    /** <code>T: any type; r: T; x: T[]; y: int :: r = x[y]</code> */
+    public static final int AGET = 38;
+
+    /** <code>T: any type; x: T; y: T[]; z: int :: x[y] = z</code> */
+    public static final int APUT = 39;
+
+    /**
+     * <code>T: any non-array object type :: r =
+     * alloc(T)</code> (allocate heap space for an object) 
+     */
+    public static final int NEW_INSTANCE = 40;
+
+    /** <code>T: any array type; r: T; x: int :: r = new T[x]</code> */
+    public static final int NEW_ARRAY = 41;
+
+    /**
+     * <code>T: any array type; r: T; x: int; v0..vx: T :: r = new T[x]
+     * {v0, ..., vx}</code> 
+     */
+    public static final int FILLED_NEW_ARRAY = 42;
+
+    /**
+     * <code>T: any object type; x: Object :: (T) x</code> (can
+     * throw <code>ClassCastException</code>) 
+     */
+    public static final int CHECK_CAST = 43;
+
+    /**
+     * <code>T: any object type; x: Object :: x instanceof
+     * T</code> 
+     */
+    public static final int INSTANCE_OF = 44;
+
+    /**
+     * <code>T: any type; r: T; x: Object; f: instance field spec of
+     * type T :: r = x.f</code> 
+     */
+    public static final int GET_FIELD = 45;
+
+    /**
+     * <code>T: any type; r: T; f: static field spec of type T :: r =
+     * f</code> 
+     */
+    public static final int GET_STATIC = 46;
+
+    /**
+     * <code>T: any type; x: T; y: Object; f: instance field spec of type
+     * T :: y.f = x</code> 
+     */
+    public static final int PUT_FIELD = 47;
+
+    /**
+     * <code>T: any type; f: static field spec of type T; x: T :: f =
+     * x</code>
+     */
+    public static final int PUT_STATIC = 48;
+
+    /**
+     * <code>Tr, T0, T1...: any types; r: Tr; m: static method spec;
+     * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)</code> (call static
+     * method) 
+     */
+    public static final int INVOKE_STATIC = 49;
+
+    /**
+     * <code>Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)</code> (call normal
+     * virtual method) 
+     */
+    public static final int INVOKE_VIRTUAL = 50;
+
+    /**
+     * <code>Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)</code> (call
+     * superclass virtual method) 
+     */
+    public static final int INVOKE_SUPER = 51;
+
+    /**
+     * <code>Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)</code> (call
+     * direct/special method) 
+     */
+    public static final int INVOKE_DIRECT = 52;
+
+    /**
+     * <code>Tr, T0, T1...: any types; r: Tr; x: Object; m: interface
+     * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1,
+     * ...)</code> (call interface method) 
+     */
+    public static final int INVOKE_INTERFACE = 53;
+
+    /**
+     * <code> T0: any type; </code> (mark beginning or end of local variable
+     * name
+     */
+    public static final int MARK_LOCAL = 54;
+
+    /**
+     * <code>T: Any type; r: T :: r = return_type</code>.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block following an invoke-*.
+     */
+    public static final int MOVE_RESULT = 55;
+
+    /**
+     * <code>T: Any type; r: T :: r = return_type</code>.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block following a non-invoke throwing insn
+     */
+    public static final int MOVE_RESULT_PSEUDO = 56;
+
+    /** <code>T: Any primitive type; v0..vx: T :: {v0, ..., vx}</code> */
+    public static final int FILL_ARRAY_DATA = 57;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private RegOps() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the name of the given opcode.
+     * 
+     * @param opcode &gt;= 0, &lt;= 255; the opcode
+     * @return non-null; its name
+     */
+    public static String opName(int opcode) {
+        switch (opcode) {
+            case NOP: return "nop";
+            case MOVE: return "move";
+            case MOVE_PARAM: return "move-param";
+            case MOVE_EXCEPTION: return "move-exception";
+            case CONST: return "const";
+            case GOTO: return "goto";
+            case IF_EQ: return "if-eq";
+            case IF_NE: return "if-ne";
+            case IF_LT: return "if-lt";
+            case IF_GE: return "if-ge";
+            case IF_LE: return "if-le";
+            case IF_GT: return "if-gt";
+            case SWITCH: return "switch";
+            case ADD: return "add";
+            case SUB: return "sub";
+            case MUL: return "mul";
+            case DIV: return "div";
+            case REM: return "rem";
+            case NEG: return "neg";
+            case AND: return "and";
+            case OR: return "or";
+            case XOR: return "xor";
+            case SHL: return "shl";
+            case SHR: return "shr";
+            case USHR: return "ushr";
+            case NOT: return "not";
+            case CMPL: return "cmpl";
+            case CMPG: return "cmpg";
+            case CONV: return "conv";
+            case TO_BYTE: return "to-byte";
+            case TO_CHAR: return "to-char";
+            case TO_SHORT: return "to-short";
+            case RETURN: return "return";
+            case ARRAY_LENGTH: return "array-length";
+            case THROW: return "throw";
+            case MONITOR_ENTER: return "monitor-enter";
+            case MONITOR_EXIT: return "monitor-exit";
+            case AGET: return "aget";
+            case APUT: return "aput";
+            case NEW_INSTANCE: return "new-instance";
+            case NEW_ARRAY: return "new-array";
+            case FILLED_NEW_ARRAY: return "filled-new-array";
+            case CHECK_CAST: return "check-cast";
+            case INSTANCE_OF: return "instance-of";
+            case GET_FIELD: return "get-field";
+            case GET_STATIC: return "get-static";
+            case PUT_FIELD: return "put-field";
+            case PUT_STATIC: return "put-static";
+            case INVOKE_STATIC: return "invoke-static";
+            case INVOKE_VIRTUAL: return "invoke-virtual";
+            case INVOKE_SUPER: return "invoke-super";
+            case INVOKE_DIRECT: return "invoke-direct";
+            case INVOKE_INTERFACE: return "invoke-interface";
+            case MOVE_RESULT: return "move-result";
+            case MOVE_RESULT_PSEUDO: return "move-result-pseudo";
+            case FILL_ARRAY_DATA: return "fill-array-data";
+        }
+
+        return "unknown-" + Hex.u1(opcode);
+    }
+
+    /**
+     * Given an IF_* RegOp, returns the right-to-left flipped version. For
+     * example, IF_GT becomes IF_LT.
+     *
+     * @param opcode An IF_* RegOp
+     * @return flipped IF Regop
+     */
+    public static int flippedIfOpcode(final int opcode) {
+        switch (opcode) {
+            case RegOps.IF_EQ:
+            case RegOps.IF_NE:
+                return opcode;
+            case RegOps.IF_LT:
+                return RegOps.IF_GT;
+            case RegOps.IF_GE:
+                return RegOps.IF_LE;
+            case RegOps.IF_LE:
+                return RegOps.IF_GE;
+            case RegOps.IF_GT:
+                return RegOps.IF_LT;
+            default:
+                throw new RuntimeException("Unrecognized IF regop: " + opcode);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java
new file mode 100644
index 0000000..09f7f18
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ToHuman;
+
+import java.util.HashMap;
+
+/**
+ * Combination of a register number and a type, used as the sources and
+ * destinations of register-based operations.
+ */
+public final class RegisterSpec
+        implements TypeBearer, ToHuman {
+    /** non-null; string to prefix register numbers with */
+    public static final String PREFIX = "v";
+
+    /** non-null; intern table for instances */
+    private static final HashMap<Object, RegisterSpec> theInterns =
+        new HashMap<Object, RegisterSpec>(1000);
+
+    /** non-null; common comparison instance used while interning */
+    private static final ForComparison theInterningItem = new ForComparison();
+
+    /** &gt;= 0; register number */
+    private final int reg;
+
+    /** non-null; type loaded or stored */
+    private final TypeBearer type;
+
+    /** null-ok; local variable info associated with this register, if any */
+    private final LocalItem local;
+
+    /**
+     * Intern the given triple as an instance of this class.
+     *
+     * @param reg &gt;= 0; the register number
+     * @param type non-null; the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local null-ok; the associated local variable, if any
+     * @return non-null; an appropriately-constructed instance
+     */
+    private static RegisterSpec intern(int reg, TypeBearer type,
+            LocalItem local) {
+        theInterningItem.set(reg, type, local);
+        RegisterSpec found = theInterns.get(theInterningItem);
+
+        if (found != null) {
+            return found;
+        }
+
+        found = theInterningItem.toRegisterSpec();
+        theInterns.put(found, found);
+        return found;
+    }
+
+    /**
+     * Returns an instance for the given register number and type, with
+     * no variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     * 
+     * @param reg &gt;= 0; the register number
+     * @param type non-null; the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpec make(int reg, TypeBearer type) {
+        return intern(reg, type, null);
+    }
+
+    /**
+     * Returns an instance for the given register number, type, and
+     * variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     *
+     * @param reg &gt;= 0; the register number
+     * @param type non-null; the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local non-null; the associated local variable
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpec make(int reg, TypeBearer type, LocalItem local) {
+        if (local == null) {
+            throw new NullPointerException("local  == null");
+        }
+
+        return intern(reg, type, local);
+    }
+
+    /**
+     * Returns an instance for the given register number, type, and
+     * variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     *
+     * @param reg &gt;= 0; the register number
+     * @param type non-null; the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local null-ok; the associated variable info or null for
+     * none
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpec makeLocalOptional(
+            int reg, TypeBearer type, LocalItem local) {
+
+        return intern(reg, type, local);
+    }
+
+    /**
+     * Gets the string form for the given register number.
+     * 
+     * @param reg &gt= 0; the register number
+     * @return non-null; the string form
+     */
+    public static String regString(int reg) {
+        return PREFIX + reg;
+    }
+
+    /**
+     * Constructs an instance. This constructor is private. Use
+     * {@link #make}.
+     * 
+     * @param reg &gt;= 0; the register number
+     * @param type non-null; the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local null-ok; the associated local variable, if any
+     */
+    private RegisterSpec(int reg, TypeBearer type, LocalItem local) {
+        if (reg < 0) {
+            throw new IllegalArgumentException("reg < 0");
+        }
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.reg = reg;
+        this.type = type;
+        this.local = local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof RegisterSpec)) {
+            if (other instanceof ForComparison) {
+                ForComparison fc = (ForComparison) other;
+                return equals(fc.reg, fc.type, fc.local);
+            }
+            return false;
+        }
+
+        RegisterSpec spec = (RegisterSpec) other;
+        return equals(spec.reg, spec.type, spec.local);
+    }
+
+    /**
+     * Helper for {@link #equals} and {@link #ForComparison.equals},
+     * which actually does the test.
+     * 
+     * @param reg value of the instance variable, for another instance
+     * @param type value of the instance variable, for another instance
+     * @param local value of the instance variable, for another instance
+     * @return whether this instance is equal to one with the given
+     * values
+     */
+    private boolean equals(int reg, TypeBearer type, LocalItem local) {
+        return (this.reg == reg)
+            && this.type.equals(type)
+            && ((this.local == local)
+                    || ((this.local != null) && this.local.equals(local)));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return hashCodeOf(reg, type, local);
+    }
+
+    /**
+     * Helper for {@link #hashCode} and {@link #ForComparison.hashCode},
+     * which actually does the calculation.
+     * 
+     * @param reg value of the instance variable
+     * @param type value of the instance variable
+     * @param local value of the instance variable
+     * @return the hash code
+     */
+    private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) {
+        int hash = (local != null) ? local.hashCode() : 0;
+
+        hash = (hash * 31 + type.hashCode()) * 31 + reg;
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toString0(false);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toString0(true);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return type.getType();
+    }
+
+    /** {@inheritDoc} */
+    public TypeBearer getFrameType() {
+        return type.getFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicType() {
+        return type.getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicFrameType() {
+        return type.getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean isConstant() {
+        return false;
+    }
+
+    /**
+     * Gets the register number.
+     * 
+     * @return &gt;= 0; the register number
+     */
+    public int getReg() {
+        return reg;
+    }
+
+    /**
+     * Gets the type (or actual value) which is loaded from or stored
+     * to the register associated with this instance.
+     * 
+     * @return non-null; the type
+     */
+    public TypeBearer getTypeBearer() {
+        return type;
+    }
+
+    /**
+     * Gets the variable info associated with this instance, if any.
+     *
+     * @return null-ok; the variable info, or <code>null</code> if this
+     * instance has none
+     */
+    public LocalItem getLocalItem() {
+        return local;
+    }
+
+    /**
+     * Gets the next available register number after the one in this
+     * instance. This is equal to the register number plus the width
+     * (category) of the type used. Among other things, this may also
+     * be used to determine the minimum required register count
+     * implied by this instance.
+     * 
+     * @return &gt;= 0; the required registers size
+     */
+    public int getNextReg() {
+        return reg + getCategory();
+    }
+
+    /**
+     * Gets the category of this instance's type. This is just a convenient
+     * shorthand for <code>getType().getCategory()</code>.
+     * 
+     * @return 1..2; the category of this instance's type
+     */
+    public int getCategory() {
+        return type.getType().getCategory();
+    }
+
+    /**
+     * Gets the string form for just the register number of this instance.
+     * 
+     * @return non-null; the register string form
+     */
+    public String regString() {
+        return regString(reg);
+    }
+
+    /**
+     * Returns an instance that is the intersection between this instance
+     * and the given one, if any. The intersection is defined as follows:
+     * 
+     * <ul>
+     *   <li>If <code>other</code> is <code>null</code>, then the result
+     *     is <code>null</code>.
+     *   <li>If the register numbers don't match, then the intersection
+     *     is <code>null</code>. Otherwise, the register number of the
+     *     intersection is the same as the one in the two instances.</li>
+     *   <li>If the types returned by <code>getType()</code> are not
+     *     <code>equals()</code>, then the intersection is null.</li>
+     *   <li>If the type bearers returned by <code>getTypeBearer()</code>
+     *     are <code>equals()</code>, then the intersection's type bearer
+     *     is the one from this instance. Otherwise, the intersection's
+     *     type bearer is the <code>getType()</code> of this instance.</li>
+     *   <li>If the locals are <code>equals()</code>, then the local info of the
+     *     intersection is the local info of this instance. Otherwise, the local info
+     *     of the intersection is <code>null</code>.</li>
+     * </ul>
+     * 
+     * @param other null-ok; instance to intersect with (or <code>null</code>)
+     * @param localPrimary whether local variables are primary to
+     * the intersection; if <code>true</code>, then the only non-null
+     * results occur when registers being intersected have equal local infos (or
+     * both have <code>null</code> local infos)
+     * @return null-ok; the intersection
+     */
+    public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
+        if (this == other) {
+            // Easy out.
+            return this;
+        }
+
+        if ((other == null) || (reg != other.getReg())) {
+            return null;
+        }
+
+        LocalItem resultLocal =
+            ((local == null) || !local.equals(other.getLocalItem())) ? null : local;
+        boolean sameName = (resultLocal == local);
+
+        if (localPrimary && !sameName) {
+            return null;
+        }
+
+        Type thisType = getType();
+        Type otherType = other.getType();
+
+        // Note: Types are always interned.
+        if (thisType != otherType) {
+            return null;
+        }
+
+        TypeBearer resultTypeBearer =
+            type.equals(other.getTypeBearer()) ? type : thisType;
+
+        if ((resultTypeBearer == type) && sameName) {
+            // It turns out that the intersection is "this" after all.
+            return this;
+        }
+
+        return (resultLocal == null) ? make(reg, resultTypeBearer) :
+            make(reg, resultTypeBearer, resultLocal);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that the
+     * register number is replaced by the given one.
+     * 
+     * @param newReg &gt;= 0; the new register number
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpec withReg(int newReg) {
+        if (reg == newReg) {
+            return this;
+        }
+
+        return makeLocalOptional(newReg, type, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the type is replaced by the given one.
+     *
+     * @param newType non-null; the new type
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpec withType(TypeBearer newType) {
+        return makeLocalOptional(reg, newType, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that the
+     * register number is offset by the given amount.
+     * 
+     * @param delta the amount to offset the register number by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpec withOffset(int delta) {
+        if (delta == 0) {
+            return this;
+        }
+
+        return withReg(reg + delta);
+    }
+    
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the type bearer is replaced by the actual underlying type
+     * (thereby stripping off non-type information) with any
+     * initialization information stripped away as well.
+     * 
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpec withSimpleType() {
+        TypeBearer orig = type;
+        Type newType;
+
+        if (orig instanceof Type) {
+            newType = (Type) orig;
+        } else {
+            newType = orig.getType();
+        }
+
+        if (newType.isUninitialized()) {
+            newType = newType.getInitializedType();
+        }
+
+        if (newType == orig) {
+            return this;
+        }
+
+        return makeLocalOptional(reg, newType, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one except that the
+     * local variable is as specified in the parameter.
+     *
+     * @param local null-ok; the local item or null for none
+     * @return an appropriate instance
+     */
+    public RegisterSpec withLocalItem(LocalItem local) {
+        if ((this.local== local)
+                    || ((this.local != null) && this.local.equals(local))) {
+
+            return this;
+        }
+
+        return makeLocalOptional(reg, type, local);
+    }
+
+
+    /**
+     * Helper for {@link #toString} and {@link #toHuman}.
+     * 
+     * @param human whether to be human-oriented
+     * @return non-null; the string form
+     */
+    private String toString0(boolean human) {
+        StringBuffer sb = new StringBuffer(40);
+
+        sb.append(regString());
+        sb.append(":");
+
+        if (local != null) {
+            sb.append(local.toString());
+        }
+
+        Type justType = type.getType();
+        sb.append(justType);
+
+        if (justType != type) {
+            sb.append("=");
+            if (human && (type instanceof Constant)) {
+                sb.append(((Constant) type).toHuman());
+            } else {
+                sb.append(type);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Holder of register spec data for the purposes of comparison (so that
+     * <code>RegisterSpec</code> itself can still keep <code>final</code>
+     * instance variables.
+     */
+    private static class ForComparison {
+        /** &gt;= 0; register number */
+        private int reg;
+        
+        /** non-null; type loaded or stored */
+        private TypeBearer type;
+
+        /** null-ok; local variable associated with this register, if any */
+        private LocalItem local;
+
+        /**
+         * Set all the instance variables.
+         * 
+         * @param reg &gt;= 0; the register number
+         * @param type non-null; the type (or possibly actual value) which
+         * is loaded from or stored to the indicated register
+         * @param local null-ok; the associated local variable, if any
+         * @return non-null; an appropriately-constructed instance
+         */
+        public void set(int reg, TypeBearer type, LocalItem local) {
+            this.reg = reg;
+            this.type = type;
+            this.local = local;
+        }
+
+        /**
+         * Construct a <code>RegisterSpec</code> of this instance's
+         * contents.
+         * 
+         * @return non-null; an appropriately-constructed instance
+         */
+        public RegisterSpec toRegisterSpec() {
+            return new RegisterSpec(reg, type, local);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof RegisterSpec)) {
+                return false;
+            }
+
+            RegisterSpec spec = (RegisterSpec) other;
+            return spec.equals(reg, type, local);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            return hashCodeOf(reg, type, local);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecList.java b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
new file mode 100644
index 0000000..28657a1
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link RegisterSpec} instances.
+ */
+public final class RegisterSpecList
+        extends FixedSizeList implements TypeList {
+    /** non-null; no-element instance */
+    public static final RegisterSpecList EMPTY = new RegisterSpecList(0);
+
+    /**
+     * Makes a single-element instance.
+     * 
+     * @param spec non-null; the element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec) {
+        RegisterSpecList result = new RegisterSpecList(1);
+        result.set(0, spec);
+        return result;
+    }
+
+    /**
+     * Makes a two-element instance.
+     * 
+     * @param spec0 non-null; the first element
+     * @param spec1 non-null; the second element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0,
+                                        RegisterSpec spec1) {
+        RegisterSpecList result = new RegisterSpecList(2);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        return result;
+    }
+
+    /**
+     * Makes a three-element instance.
+     * 
+     * @param spec0 non-null; the first element
+     * @param spec1 non-null; the second element
+     * @param spec2 non-null; the third element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+                                        RegisterSpec spec2) {
+        RegisterSpecList result = new RegisterSpecList(3);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        result.set(2, spec2);
+        return result;
+    }
+
+    /**
+     * Makes a four-element instance.
+     * 
+     * @param spec0 non-null; the first element
+     * @param spec1 non-null; the second element
+     * @param spec2 non-null; the third element
+     * @param spec3 non-null; the fourth element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+                                        RegisterSpec spec2,
+                                        RegisterSpec spec3) {
+        RegisterSpecList result = new RegisterSpecList(4);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        result.set(2, spec2);
+        result.set(3, spec3);
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public RegisterSpecList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType(int n) {
+        return get(n).getType().getType();
+    }
+
+    /** {@inheritDoc} */
+    public int getWordCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            result += getType(i).getCategory();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList withAddedType(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+    
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return non-null; the indicated element
+     */
+    public RegisterSpec get(int n) {
+        return (RegisterSpec) get0(n);
+    }
+
+    /**
+     * Returns a RegisterSpec in this list that uses the specified register,
+     * or null if there is none in this list.
+     * @param reg Register to find
+     * @return RegisterSpec that uses argument or null.
+     */
+    public RegisterSpec specForRegister(int reg) {
+        int sz = size();
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec rs;
+
+            rs = get(i);
+
+            if (rs.getReg() == reg) {
+                return rs;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the index of a RegisterSpec in this list that uses the specified
+     * register, or -1 if none in this list uses the register.
+     * @param reg Register to find
+     * @return index of RegisterSpec or -1
+     */
+    public int indexOfRegister(int reg) {
+        int sz = size();
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec rs;
+
+            rs = get(i);
+
+            if (rs.getReg() == reg) {
+                return i;
+            }
+        }
+
+        return -1;        
+    }
+    
+    /**
+     * Sets the element at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param spec non-null; the value to store
+     */
+    public void set(int n, RegisterSpec spec) {
+        set0(n, spec);
+    }
+
+    /**
+     * Gets the minimum required register count implied by this
+     * instance. This is equal to the highest register number referred
+     * to plus the widest width (largest category) of the type used in
+     * that register.
+     * 
+     * @return &gt;= 0; the required registers size
+     */
+    public int getRegistersSize() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec spec = (RegisterSpec) get0(i);
+            if (spec != null) {
+                int min = spec.getNextReg();
+                if (min > result) {
+                    result = min;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that it has an additional element prepended to the original.
+     * Mutability of the result is inherited from the original.
+     * 
+     * @param spec non-null; the new first spec (to prepend)
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecList withFirst(RegisterSpec spec) {
+        int sz = size();
+        RegisterSpecList result = new RegisterSpecList(sz + 1);
+
+        for (int i = 0; i < sz; i++) {
+            result.set0(i + 1, get0(i));
+        }
+
+        result.set0(0, spec);
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that its first element is removed. Mutability of the
+     * result is inherited from the original.
+     * 
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecList withoutFirst() {
+        int newSize = size() - 1;
+
+        if (newSize == 0) {
+            return EMPTY;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(newSize);
+
+        for (int i = 0; i < newSize; i++) {
+            result.set0(i, get0(i + 1));
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that its last element is removed. Mutability of the
+     * result is inherited from the original.
+     * 
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecList withoutLast() {
+        int newSize = size() - 1;
+
+        if (newSize == 0) {
+            return EMPTY;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(newSize);
+
+        for (int i = 0; i < newSize; i++) {
+            result.set0(i, get0(i));
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are offset by the given amount. Mutability
+     * of the result is inherited from the original.
+     * 
+     * @param delta the amount to offset the register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecList withOffset(int delta) {
+        int sz = size();
+
+        if (sz == 0) {
+            // Don't bother making a new zero-element instance.
+            return this;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = (RegisterSpec) get0(i);
+            if (one != null) {
+                result.set0(i, one.withOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are renumbered sequentially from the given
+     * base, with the first number duplicated if indicated. 
+     * 
+     * @param base the base register number
+     * @param duplicateFirst whether to duplicate the first number
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecList withSequentialRegisters(int base,
+                                                    boolean duplicateFirst) {
+        int sz = size();
+
+        if (sz == 0) {
+            // Don't bother making a new zero-element instance.
+            return this;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = (RegisterSpec) get0(i);
+            result.set0(i, one.withReg(base));
+            if (duplicateFirst) {
+                duplicateFirst = false;
+            } else {
+                base += one.getCategory();
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
new file mode 100644
index 0000000..4eb2f65
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Set of {@link RegisterSpec} instances, where a given register number
+ * may appear only once in the set.
+ */
+public final class RegisterSpecSet
+        extends MutabilityControl {
+    /** non-null; no-element instance */
+    public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0);
+
+    /**
+     * non-null; array of register specs, where each element is
+     * <code>null</code> or is an instance whose <code>reg</code>
+     * matches the array index 
+     */
+    private final RegisterSpec[] specs;
+
+    /** &gt;= -1; size of the set or <code>-1</code> if not yet calculated */
+    private int size;
+
+    /**
+     * Constructs an instance. The instance is initially empty.
+     * 
+     * @param maxSize &gt;= 0; the maximum register number (exclusive) that
+     * may be represented in this instance
+     */
+    public RegisterSpecSet(int maxSize) {
+        super(maxSize != 0);
+
+        this.specs = new RegisterSpec[maxSize];
+        this.size = 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof RegisterSpecSet)) {
+            return false;
+        }
+
+        RegisterSpecSet otherSet = (RegisterSpecSet) other;
+        RegisterSpec[] otherSpecs = otherSet.specs;
+        int len = specs.length;
+
+        if ((len != otherSpecs.length) || (size() != otherSet.size())) {
+            return false;
+        }
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec s1 = specs[i];
+            RegisterSpec s2 = otherSpecs[i];
+
+            if (s1 == s2) {
+                continue;
+            }
+
+            if ((s1 == null) || !s1.equals(s2)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int len = specs.length;
+        int hash = 0;
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            int oneHash = (spec == null) ? 0 : spec.hashCode();
+            hash = (hash * 31) + oneHash;
+        }
+
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int len = specs.length;
+        StringBuffer sb = new StringBuffer(len * 25);
+
+        sb.append('{');
+
+        boolean any = false;
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                if (any) {
+                    sb.append(", ");
+                } else {
+                    any = true;
+                }
+                sb.append(spec);
+            }
+        }
+
+        sb.append('}');
+        return sb.toString();
+    }       
+
+    /**
+     * Gets the maximum number of registers that may be in this instance, which
+     * is also the maximum-plus-one of register numbers that may be
+     * represented.
+     * 
+     * @return &gt;= 0; the maximum size
+     */
+    public int getMaxSize() {
+        return specs.length;
+    }
+
+    /**
+     * Gets the current size of this instance.
+     * 
+     * @return &gt;= 0; the size
+     */
+    public int size() {
+        int result = size;
+
+        if (result < 0) {
+            int len = specs.length;
+
+            result = 0;
+            for (int i = 0; i < len; i++) {
+                if (specs[i] != null) {
+                    result++;
+                }
+            }
+
+            size = result;
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the element with the given register number, if any.
+     * 
+     * @param reg &gt;= 0; the desired register number
+     * @return null-ok; the element with the given register number or
+     * <code>null</code> if there is none
+     */
+    public RegisterSpec get(int reg) {
+        try {
+            return specs[reg];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus reg");
+        }
+    }
+
+    /**
+     * Gets the element with the same register number as the given
+     * spec, if any. This is just a convenient shorthand for
+     * <code>get(spec.getReg())</code>.
+     * 
+     * @param spec non-null; spec with the desired register number
+     * @return null-ok; the element with the matching register number or
+     * <code>null</code> if there is none
+     */
+    public RegisterSpec get(RegisterSpec spec) {
+        return get(spec.getReg());
+    }
+
+    /**
+     * Returns the spec in this set that's currently associated with a given
+     * name, or null if there is none.
+     *
+     * @param local non-null; local item to search for
+     * @return null-ok; first register found with name.
+     */
+    public RegisterSpec localItemToSpec(LocalItem local) {
+        for (int reg = 0; reg < specs.length; reg++) {
+            if (specs[reg] != null && local.equals(specs[reg].getLocalItem())) {
+                return specs[reg];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes a spec from the set. Only the register number
+     * of the parameter is significant.
+     *
+     * @param toRemove non-null; register to remove.
+     */
+    public void remove(RegisterSpec toRemove) {
+        try {
+            specs[toRemove.getReg()] = null;
+            size = -1;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus reg");
+        }
+    }
+
+    /**
+     * Puts the given spec into the set. If there is already an element in
+     * the set with the same register number, it is replaced. Additionally,
+     * if the previous element is for a category-2 register, then that
+     * previous element is nullified. Finally, if the given spec is for
+     * a category-2 register, then the immediately subsequent element
+     * is nullified.
+     * 
+     * @param spec non-null; the register spec to put in the instance
+     */
+    public void put(RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        size = -1;
+
+        try {
+            int reg = spec.getReg();
+            specs[reg] = spec;
+
+            if (reg > 0) {
+                int prevReg = reg - 1;
+                RegisterSpec prevSpec = specs[prevReg];
+                if ((prevSpec != null) && (prevSpec.getCategory() == 2)) {
+                    specs[prevReg] = null;
+                }
+            }
+
+            if (spec.getCategory() == 2) {
+                specs[reg + 1] = null;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("spec.getReg() out of range");
+        }
+    }
+
+    /**
+     * Intersects this instance with the given one, modifying this
+     * instance. The intersection consists of the pairwise
+     * {@link RegisterSpec#intersect} of corresponding elements from
+     * this instance and the given one where both are non-null.
+     * 
+     * @param other non-null; set to intersect with
+     * @param localPrimary whether local variables are primary to
+     * the intersection; if <code>true</code>, then the only non-null
+     * result elements occur when registers being intersected have
+     * equal names (or both have <code>null</code> names)
+     */
+    public void intersect(RegisterSpecSet other, boolean localPrimary) {
+        throwIfImmutable();
+
+        RegisterSpec[] otherSpecs = other.specs;
+        int thisLen = specs.length;
+        int len = Math.min(thisLen, otherSpecs.length);
+
+        size = -1;
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+
+            if (spec == null) {
+                continue;
+            }
+
+            RegisterSpec intersection =
+                spec.intersect(otherSpecs[i], localPrimary);
+            if (intersection != spec) {
+                specs[i] = intersection;
+            }
+        }
+
+        for (int i = len; i < thisLen; i++) {
+            specs[i] = null;
+        }
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are offset by the given amount. Mutability
+     * of the result is inherited from the original.
+     * 
+     * @param delta the amount to offset the register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RegisterSpecSet withOffset(int delta) {
+        int len = specs.length;
+        RegisterSpecSet result = new RegisterSpecSet(len + delta);
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                result.put(spec.withOffset(delta));
+            }
+        }
+
+        result.size = size;
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Makes and return a mutable copy of this instance.
+     * 
+     * @return non-null; the mutable copy
+     */
+    public RegisterSpecSet mutableCopy() {
+        int len = specs.length;
+        RegisterSpecSet copy = new RegisterSpecSet(len);
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                copy.put(spec);
+            }
+        }
+
+        copy.size = size;
+
+        return copy;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rop.java b/dx/src/com/android/dx/rop/code/Rop.java
new file mode 100644
index 0000000..f918e12
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rop.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Class that describes all the immutable parts of register-based operations.
+ */
+public final class Rop {
+    /** minimum <code>BRANCH_*</code> value */
+    public static final int BRANCH_MIN = 1;
+
+    /** indicates a non-branching op */
+    public static final int BRANCH_NONE = 1;
+
+    /** indicates a function/method return */
+    public static final int BRANCH_RETURN = 2;
+
+    /** indicates an unconditional goto */
+    public static final int BRANCH_GOTO = 3;
+
+    /** indicates a two-way branch */
+    public static final int BRANCH_IF = 4;
+
+    /** indicates a switch-style branch */
+    public static final int BRANCH_SWITCH = 5;
+
+    /** indicates a throw-style branch (both always-throws and may-throw) */
+    public static final int BRANCH_THROW = 6;
+
+    /** maximum <code>BRANCH_*</code> value */
+    public static final int BRANCH_MAX = 6;
+
+    /** the opcode; one of the constants in {@link RegOps} */
+    private final int opcode;
+
+    /**
+     * non-null; result type of this operation; {@link Type#VOID} for
+     * no-result operations 
+     */
+    private final Type result;
+
+    /** non-null; types of all the sources of this operation */
+    private final TypeList sources;
+
+    /** non-null; list of possible types thrown by this operation */
+    private final TypeList exceptions;
+
+    /**
+     * the branchingness of this op; one of the <code>BRANCH_*</code>
+     * constants in this class 
+     */
+    private final int branchingness;
+
+    /** whether this is a function/method call op or similar */
+    private final boolean isCallLike;
+
+    /** null-ok; nickname, if specified (used for debugging) */
+    private final String nickname;
+
+    /**
+     * Constructs an instance. This method is private. Use one of the
+     * public constructors.
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result non-null; result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources non-null; types of all the sources of this operation
+     * @param exceptions non-null; list of possible types thrown by this
+     * operation
+     * @param branchingness the branchingness of this op; one of the
+     * <code>BRANCH_*</code> constants
+     * @param isCallLike whether the op is a function/method call or similar
+     * @param nickname null-ok; optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources,
+               TypeList exceptions, int branchingness, boolean isCallLike,
+               String nickname) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        if (sources == null) {
+            throw new NullPointerException("sources == null");
+        }
+
+        if (exceptions == null) {
+            throw new NullPointerException("exceptions == null");
+        }
+
+        if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
+            throw new IllegalArgumentException("exceptions / branchingness " +
+                                               "mismatch");
+        }
+
+        this.opcode = opcode;
+        this.result = result;
+        this.sources = sources;
+        this.exceptions = exceptions;
+        this.branchingness = branchingness;
+        this.isCallLike = isCallLike;
+        this.nickname = nickname;
+    }
+
+    /**
+     * Constructs an instance. The constructed instance is never a 
+     * call-like op (see {@link #isCallLike}).
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result non-null; result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources non-null; types of all the sources of this operation
+     * @param exceptions non-null; list of possible types thrown by this
+     * operation
+     * @param branchingness the branchingness of this op; one of the
+     * <code>BRANCH_*</code> constants
+     * @param nickname null-ok; optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources,
+               TypeList exceptions, int branchingness, String nickname) {
+        this(opcode, result, sources, exceptions, branchingness, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a no-exception instance. The constructed instance is never a 
+     * call-like op (see {@link #isCallLike}).
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result non-null; result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources non-null; types of all the sources of this operation
+     * @param branchingness the branchingness of this op; one of the
+     * <code>BRANCH_*</code> constants
+     * @param nickname null-ok; optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, int branchingness,
+               String nickname) {
+        this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a non-branching no-exception instance. The
+     * <code>branchingness</code> is always <code>BRANCH_NONE</code>,
+     * and it is never a call-like op (see {@link #isCallLike}).
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result non-null; result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources non-null; types of all the sources of this operation
+     * @param nickname null-ok; optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, String nickname) {
+        this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
+             false, nickname);
+    }
+
+    /**
+     * Constructs a non-empty exceptions instance. Its
+     * <code>branchingness</code> is always <code>BRANCH_THROW</code>,
+     * but it is never a call-like op (see {@link #isCallLike}).
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result non-null; result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources non-null; types of all the sources of this operation
+     * @param exceptions non-null; list of possible types thrown by this
+     * operation
+     * @param nickname null-ok; optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
+               String nickname) {
+        this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a non-nicknamed instance with non-empty exceptions, which
+     * is always a call-like op (see {@link #isCallLike}). Its
+     * <code>branchingness</code> is always <code>BRANCH_THROW</code>.
+     * 
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param sources non-null; types of all the sources of this operation
+     * @param exceptions non-null; list of possible types thrown by this
+     * operation
+     */
+    public Rop(int opcode, TypeList sources, TypeList exceptions) {
+        this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
+             null);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            // Easy out.
+            return true;
+        }
+
+        if (!(other instanceof Rop)) {
+            return false;
+        }
+
+        Rop rop = (Rop) other;
+
+        return (opcode == rop.opcode) &&
+            (branchingness == rop.branchingness) &&
+            (result == rop.result) &&
+            sources.equals(rop.sources) &&
+            exceptions.equals(rop.exceptions);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int h = (opcode * 31) + branchingness;
+        h = (h * 31) + result.hashCode();
+        h = (h * 31) + sources.hashCode();
+        h = (h * 31) + exceptions.hashCode();
+
+        return h;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(40);
+
+        sb.append("Rop{");
+
+        sb.append(RegOps.opName(opcode));
+
+        if (result != Type.VOID) {
+            sb.append(" ");
+            sb.append(result);
+        } else {
+            sb.append(" .");
+        }
+
+        sb.append(" <-");
+
+        int sz = sources.size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(' ');
+                sb.append(sources.getType(i));
+            }
+        }
+
+        if (isCallLike) {
+            sb.append(" call");
+        }
+
+        sz = exceptions.size();
+        if (sz != 0) {
+            sb.append(" throws");
+            for (int i = 0; i < sz; i++) {
+                sb.append(' ');
+                Type one = exceptions.getType(i);
+                if (one == Type.THROWABLE) {
+                    sb.append("<any>");
+                } else {
+                    sb.append(exceptions.getType(i));
+                }
+            }
+        } else {
+            switch (branchingness) {
+                case BRANCH_NONE:   sb.append(" flows"); break;
+                case BRANCH_RETURN: sb.append(" returns"); break;
+                case BRANCH_GOTO:   sb.append(" gotos"); break;
+                case BRANCH_IF:     sb.append(" ifs"); break;
+                case BRANCH_SWITCH: sb.append(" switches"); break;
+                default: sb.append(" " + Hex.u1(branchingness)); break;
+            }
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the opcode.
+     * 
+     * @return the opcode
+     */
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the result type. A return value of {@link Type#VOID}
+     * means this operation returns nothing.
+     * 
+     * @return null-ok; the result spec
+     */
+    public Type getResult() {
+        return result;
+    }
+
+    /**
+     * Gets the source types.
+     * 
+     * @return non-null; the source types
+     */
+    public TypeList getSources() {
+        return sources;
+    }
+
+    /**
+     * Gets the list of exception types that might be thrown.
+     * 
+     * @return non-null; the list of exception types
+     */
+    public TypeList getExceptions() {
+        return exceptions;
+    }
+
+    /**
+     * Gets the branchingness of this instance.
+     * 
+     * @return the branchingness
+     */
+    public int getBranchingness() {
+        return branchingness;
+    }
+
+    /**
+     * Gets whether this opcode is a function/method call or similar.
+     * 
+     * @return <code>true</code> iff this opcode is call-like
+     */
+    public boolean isCallLike() {
+        return isCallLike;
+    }
+
+
+    /**
+     * Gets whether this opcode is commutative (the order of its sources are
+     * unimportant) or not. All commutative Rops have exactly two sources and
+     * have no branchiness.
+     *
+     * @return true if rop is commutative
+     */
+    public boolean isCommutative() {
+        switch (opcode) {
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+            case RegOps.ADD:
+            case RegOps.MUL:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Gets the nickname. If this instance has no nickname, this returns
+     * the result of calling {@link #toString}.
+     * 
+     * @return non-null; the nickname
+     */
+    public String getNickname() {
+        if (nickname != null) {
+            return nickname;
+        }
+
+        return toString();
+    }
+
+    /**
+     * Gets whether this operation can possibly throw an exception. This
+     * is just a convenient wrapper for
+     * <code>getExceptions().size() != 0</code>.
+     * 
+     * @return <code>true</code> iff this operation can possibly throw
+     */
+    public final boolean canThrow() {
+        return (exceptions.size() != 0);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RopMethod.java b/dx/src/com/android/dx/rop/code/RopMethod.java
new file mode 100644
index 0000000..0c0d8f1
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RopMethod.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * All of the parts that make up a method at the rop layer.
+ */
+public final class RopMethod {
+    /** non-null; basic block list of the method */
+    private final BasicBlockList blocks;
+
+    /** &gt;= 0; label for the block which starts the method */
+    private final int firstLabel;
+
+    /**
+     * null-ok; array of predecessors for each block, indexed by block
+     * label 
+     */
+    private IntList[] predecessors;
+
+    /**
+     * null-ok; the predecessors for the implicit "exit" block, that is
+     * the labels for the blocks that return, if calculated 
+     */
+    private IntList exitPredecessors;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param blocks non-null; basic block list of the method
+     * @param firstLabel &gt;= 0; the label of the first block to execute
+     */
+    public RopMethod(BasicBlockList blocks, int firstLabel) {
+        if (blocks == null) {
+            throw new NullPointerException("blocks == null");
+        }
+
+        if (firstLabel < 0) {
+            throw new IllegalArgumentException("firstLabel < 0");
+        }
+
+        this.blocks = blocks;
+        this.firstLabel = firstLabel;
+
+        this.predecessors = null;
+        this.exitPredecessors = null;
+    }
+
+    /**
+     * Gets the basic block list for this method.
+     * 
+     * @return non-null; the list
+     */
+    public BasicBlockList getBlocks() {
+        return blocks;
+    }
+
+    /**
+     * Gets the label for the first block in the method that this list
+     * represents.
+     * 
+     * @return &gt;= 0; the first-block label
+     */
+    public int getFirstLabel() {
+        return firstLabel;
+    }
+
+    /**
+     * Gets the predecessors associated with the given block. This throws
+     * an exception if there is no block with the given label.
+     * 
+     * @param label &gt;= 0; the label of the block in question
+     * @return non-null; the predecessors of that block
+     */
+    public IntList labelToPredecessors(int label) {
+        if (exitPredecessors == null) {
+            calcPredecessors();
+        }
+
+        IntList result = predecessors[label];
+
+        if (result == null) {
+            throw new RuntimeException("no such block: " + Hex.u2(label));
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the exit predecessors for this instance.
+     * 
+     * @return non-null; the exit predecessors
+     */
+    public IntList getExitPredecessors() {
+        if (exitPredecessors == null) {
+            calcPredecessors();
+        }
+
+        return exitPredecessors;
+    }
+
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount.
+     * 
+     * @param delta the amount to offset register numbers by
+     * @return non-null; an appropriately-constructed instance
+     */
+    public RopMethod withRegisterOffset(int delta) {
+        RopMethod result = new RopMethod(blocks.withRegisterOffset(delta),
+                                         firstLabel);
+
+        if (exitPredecessors != null) {
+            /*
+             * The predecessors have been calculated. It's safe to
+             * inject these into the new instance, since the
+             * transformation being applied doesn't affect the
+             * predecessors.
+             */
+            result.exitPredecessors = exitPredecessors;
+            result.predecessors = predecessors;
+        }
+
+        return result;
+    }
+
+    /**
+     * Calculates the predecessor sets for each block as well as for the
+     * exit.
+     */
+    private void calcPredecessors() {
+        int maxLabel = blocks.getMaxLabel();
+        IntList[] predecessors = new IntList[maxLabel];
+        IntList exitPredecessors = new IntList(10);
+        int sz = blocks.size();
+
+        /*
+         * For each block, find its successors, and add the block's label to
+         * the successor's predecessors.
+         */
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            int label = one.getLabel();
+            IntList successors = one.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                // This block exits.
+                exitPredecessors.add(label);
+            } else {
+                for (int j = 0; j < ssz; j++) {
+                    int succLabel = successors.get(j);
+                    IntList succPreds = predecessors[succLabel];
+                    if (succPreds == null) {
+                        succPreds = new IntList(10);
+                        predecessors[succLabel] = succPreds;
+                    }
+                    succPreds.add(label);
+                }
+            }
+        }
+
+        // Sort and immutablize all the predecessor lists.
+        for (int i = 0; i < maxLabel; i++) {
+            IntList preds = predecessors[i];
+            if (preds != null) {
+                preds.sort();
+                preds.setImmutable();
+            }
+        }
+
+        exitPredecessors.sort();
+        exitPredecessors.setImmutable();
+
+        /*
+         * The start label might not ever have had any predecessors
+         * added to it (probably doesn't, because of how Java gets
+         * translated into rop form). So, check for this and rectify
+         * the situation if required.
+         */
+        if (predecessors[firstLabel] == null) {
+            predecessors[firstLabel] = IntList.EMPTY;
+        }
+
+        this.predecessors = predecessors;
+        this.exitPredecessors = exitPredecessors;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rops.java b/dx/src/com/android/dx/rop/code/Rops.java
new file mode 100644
index 0000000..b662656
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rops.java
@@ -0,0 +1,2124 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Standard instances of {@link Rop}.
+ */
+public final class Rops {
+    /** <code>nop()</code> */
+    public static final Rop NOP =
+        new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop");
+
+    /** <code>r,x: int :: r = x;</code> */
+    public static final Rop MOVE_INT =
+        new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int");
+
+    /** <code>r,x: long :: r = x;</code> */
+    public static final Rop MOVE_LONG =
+        new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long");
+
+    /** <code>r,x: float :: r = x;</code> */
+    public static final Rop MOVE_FLOAT =
+        new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float");
+
+    /** <code>r,x: double :: r = x;</code> */
+    public static final Rop MOVE_DOUBLE =
+        new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double");
+
+    /** <code>r,x: Object :: r = x;</code> */
+    public static final Rop MOVE_OBJECT =
+        new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object");
+
+    /**
+     * <code>r,x: ReturnAddress :: r = x;</code>
+     *
+     * Note that this rop-form instruction has no dex-form equivilent and
+     * must be removed before the dex conversion.
+     */
+    public static final Rop MOVE_RETURN_ADDRESS =
+        new Rop(RegOps.MOVE, Type.RETURN_ADDRESS,
+                StdTypeList.RETURN_ADDRESS, "move-return-address");
+
+    /** <code>r,param(x): int :: r = param(x);</code> */
+    public static final Rop MOVE_PARAM_INT =
+        new Rop(RegOps.MOVE_PARAM, Type.INT, StdTypeList.EMPTY,
+                "move-param-int");
+
+    /** <code>r,param(x): long :: r = param(x);</code> */
+    public static final Rop MOVE_PARAM_LONG =
+        new Rop(RegOps.MOVE_PARAM, Type.LONG, StdTypeList.EMPTY,
+                "move-param-long");
+
+    /** <code>r,param(x): float :: r = param(x);</code> */
+    public static final Rop MOVE_PARAM_FLOAT =
+        new Rop(RegOps.MOVE_PARAM, Type.FLOAT, StdTypeList.EMPTY,
+                "move-param-float");
+
+    /** <code>r,param(x): double :: r = param(x);</code> */
+    public static final Rop MOVE_PARAM_DOUBLE =
+        new Rop(RegOps.MOVE_PARAM, Type.DOUBLE, StdTypeList.EMPTY,
+                "move-param-double");
+
+    /** <code>r,param(x): Object :: r = param(x);</code> */
+    public static final Rop MOVE_PARAM_OBJECT =
+        new Rop(RegOps.MOVE_PARAM, Type.OBJECT, StdTypeList.EMPTY,
+                "move-param-object");
+
+    /** <code>r, literal: int :: r = literal;</code> */
+    public static final Rop CONST_INT =
+        new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int");
+
+    /** <code>r, literal: long :: r = literal;</code> */
+    public static final Rop CONST_LONG =
+        new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long");
+
+    /** <code>r, literal: float :: r = literal;</code> */
+    public static final Rop CONST_FLOAT =
+        new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float");
+
+    /** <code>r, literal: double :: r = literal;</code> */
+    public static final Rop CONST_DOUBLE =
+        new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double");
+
+    /** <code>r, literal: Object :: r = literal;</code> */
+    public static final Rop CONST_OBJECT =
+        new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "const-object");
+
+    /** <code>r, literal: Object :: r = literal;</code> */
+    public static final Rop CONST_OBJECT_NOTHROW =
+        new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+                "const-object-nothrow");
+
+    /** <code>goto <i>label</i></code> */
+    public static final Rop GOTO =
+        new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO,
+                "goto");
+
+    /** <code>x: int :: if (x == 0) goto <i>label</i></code> */
+    public static final Rop IF_EQZ_INT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-eqz-int");
+
+    /** <code>x: int :: if (x != 0) goto <i>label</i></code> */
+    public static final Rop IF_NEZ_INT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-nez-int");
+
+    /** <code>x: int :: if (x &lt; 0) goto <i>label</i></code> */
+    public static final Rop IF_LTZ_INT =
+        new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-ltz-int");
+
+    /** <code>x: int :: if (x &gt;= 0) goto <i>label</i></code> */
+    public static final Rop IF_GEZ_INT =
+        new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-gez-int");
+
+    /** <code>x: int :: if (x &lt;= 0) goto <i>label</i></code> */
+    public static final Rop IF_LEZ_INT =
+        new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-lez-int");
+
+    /** <code>x: int :: if (x &gt; 0) goto <i>label</i></code> */
+    public static final Rop IF_GTZ_INT =
+        new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-gtz-int");
+
+    /** <code>x: Object :: if (x == null) goto <i>label</i></code> */
+    public static final Rop IF_EQZ_OBJECT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+                "if-eqz-object");
+
+    /** <code>x: Object :: if (x != null) goto <i>label</i></code> */
+    public static final Rop IF_NEZ_OBJECT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+                "if-nez-object");
+
+    /** <code>x,y: int :: if (x == y) goto <i>label</i></code> */
+    public static final Rop IF_EQ_INT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-eq-int");
+
+    /** <code>x,y: int :: if (x != y) goto <i>label</i></code> */
+    public static final Rop IF_NE_INT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-ne-int");
+
+    /** <code>x,y: int :: if (x &lt; y) goto <i>label</i></code> */
+    public static final Rop IF_LT_INT =
+        new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-lt-int");
+
+    /** <code>x,y: int :: if (x &gt;= y) goto <i>label</i></code> */
+    public static final Rop IF_GE_INT =
+        new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-ge-int");
+
+    /** <code>x,y: int :: if (x &lt;= y) goto <i>label</i></code> */
+    public static final Rop IF_LE_INT =
+        new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-le-int");
+
+    /** <code>x,y: int :: if (x &gt; y) goto <i>label</i></code> */
+    public static final Rop IF_GT_INT =
+        new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-gt-int");
+
+    /** <code>x,y: Object :: if (x == y) goto <i>label</i></code> */
+    public static final Rop IF_EQ_OBJECT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Rop.BRANCH_IF, "if-eq-object");
+
+    /** <code>x,y: Object :: if (x != y) goto <i>label</i></code> */
+    public static final Rop IF_NE_OBJECT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Rop.BRANCH_IF, "if-ne-object");
+
+    /** <code>x: int :: goto switchtable[x]</code> */
+    public static final Rop SWITCH = 
+        new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH,
+                "switch");
+
+    /** <code>r,x,y: int :: r = x + y;</code> */
+    public static final Rop ADD_INT =
+        new Rop(RegOps.ADD, Type.INT, StdTypeList.INT_INT, "add-int");
+
+    /** <code>r,x,y: long :: r = x + y;</code> */
+    public static final Rop ADD_LONG =
+        new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG_LONG, "add-long");
+
+    /** <code>r,x,y: float :: r = x + y;</code> */
+    public static final Rop ADD_FLOAT =
+        new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "add-float");
+
+    /** <code>r,x,y: double :: r = x + y;</code> */
+    public static final Rop ADD_DOUBLE =
+        new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "add-double");
+
+    /** <code>r,x,y: int :: r = x - y;</code> */
+    public static final Rop SUB_INT =
+        new Rop(RegOps.SUB, Type.INT, StdTypeList.INT_INT, "sub-int");
+
+    /** <code>r,x,y: long :: r = x - y;</code> */
+    public static final Rop SUB_LONG =
+        new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG_LONG, "sub-long");
+
+    /** <code>r,x,y: float :: r = x - y;</code> */
+    public static final Rop SUB_FLOAT =
+        new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "sub-float");
+
+    /** <code>r,x,y: double :: r = x - y;</code> */
+    public static final Rop SUB_DOUBLE =
+        new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "sub-double");
+
+    /** <code>r,x,y: int :: r = x * y;</code> */
+    public static final Rop MUL_INT =
+        new Rop(RegOps.MUL, Type.INT, StdTypeList.INT_INT, "mul-int");
+
+    /** <code>r,x,y: long :: r = x * y;</code> */
+    public static final Rop MUL_LONG =
+        new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG_LONG, "mul-long");
+
+    /** <code>r,x,y: float :: r = x * y;</code> */
+    public static final Rop MUL_FLOAT =
+        new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "mul-float");
+
+    /** <code>r,x,y: double :: r = x * y;</code> */
+    public static final Rop MUL_DOUBLE =
+        new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "mul-double");
+
+    /** <code>r,x,y: int :: r = x / y;</code> */
+    public static final Rop DIV_INT =
+        new Rop(RegOps.DIV, Type.INT, StdTypeList.INT_INT,
+                Exceptions.LIST_Error_ArithmeticException, "div-int");
+
+    /** <code>r,x,y: long :: r = x / y;</code> */
+    public static final Rop DIV_LONG =
+        new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG_LONG,
+                Exceptions.LIST_Error_ArithmeticException, "div-long");
+
+    /** <code>r,x,y: float :: r = x / y;</code> */
+    public static final Rop DIV_FLOAT =
+        new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "div-float");
+
+    /** <code>r,x,y: double :: r = x / y;</code> */
+    public static final Rop DIV_DOUBLE =
+        new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                "div-double");
+
+    /** <code>r,x,y: int :: r = x % y;</code> */
+    public static final Rop REM_INT =
+        new Rop(RegOps.REM, Type.INT, StdTypeList.INT_INT,
+                Exceptions.LIST_Error_ArithmeticException, "rem-int");
+
+    /** <code>r,x,y: long :: r = x % y;</code> */
+    public static final Rop REM_LONG =
+        new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG_LONG,
+                Exceptions.LIST_Error_ArithmeticException, "rem-long");
+
+    /** <code>r,x,y: float :: r = x % y;</code> */
+    public static final Rop REM_FLOAT =
+        new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "rem-float");
+
+    /** <code>r,x,y: double :: r = x % y;</code> */
+    public static final Rop REM_DOUBLE =
+        new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                "rem-double");
+
+    /** <code>r,x: int :: r = -x;</code> */
+    public static final Rop NEG_INT =
+        new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int");
+
+    /** <code>r,x: long :: r = -x;</code> */
+    public static final Rop NEG_LONG =
+        new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long");
+
+    /** <code>r,x: float :: r = -x;</code> */
+    public static final Rop NEG_FLOAT =
+        new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float");
+
+    /** <code>r,x: double :: r = -x;</code> */
+    public static final Rop NEG_DOUBLE =
+        new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double");
+
+    /** <code>r,x,y: int :: r = x &amp; y;</code> */
+    public static final Rop AND_INT =
+        new Rop(RegOps.AND, Type.INT, StdTypeList.INT_INT, "and-int");
+
+    /** <code>r,x,y: long :: r = x &amp; y;</code> */
+    public static final Rop AND_LONG =
+        new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG_LONG, "and-long");
+
+    /** <code>r,x,y: int :: r = x | y;</code> */
+    public static final Rop OR_INT =
+        new Rop(RegOps.OR, Type.INT, StdTypeList.INT_INT, "or-int");
+
+    /** <code>r,x,y: long :: r = x | y;</code> */
+    public static final Rop OR_LONG =
+        new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG_LONG, "or-long");
+
+    /** <code>r,x,y: int :: r = x ^ y;</code> */
+    public static final Rop XOR_INT =
+        new Rop(RegOps.XOR, Type.INT, StdTypeList.INT_INT, "xor-int");
+
+    /** <code>r,x,y: long :: r = x ^ y;</code> */
+    public static final Rop XOR_LONG =
+        new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG_LONG, "xor-long");
+
+    /** <code>r,x,y: int :: r = x &lt;&lt; y;</code> */
+    public static final Rop SHL_INT =
+        new Rop(RegOps.SHL, Type.INT, StdTypeList.INT_INT, "shl-int");
+
+    /** <code>r,x: long; y: int :: r = x &lt;&lt; y;</code> */
+    public static final Rop SHL_LONG =
+        new Rop(RegOps.SHL, Type.LONG, StdTypeList.LONG_INT, "shl-long");
+
+    /** <code>r,x,y: int :: r = x &gt;&gt; y;</code> */
+    public static final Rop SHR_INT =
+        new Rop(RegOps.SHR, Type.INT, StdTypeList.INT_INT, "shr-int");
+
+    /** <code>r,x: long; y: int :: r = x &gt;&gt; y;</code> */
+    public static final Rop SHR_LONG =
+        new Rop(RegOps.SHR, Type.LONG, StdTypeList.LONG_INT, "shr-long");
+
+    /** <code>r,x,y: int :: r = x &gt;&gt;&gt; y;</code> */
+    public static final Rop USHR_INT =
+        new Rop(RegOps.USHR, Type.INT, StdTypeList.INT_INT, "ushr-int");
+
+    /** <code>r,x: long; y: int :: r = x &gt;&gt;&gt; y;</code> */
+    public static final Rop USHR_LONG =
+        new Rop(RegOps.USHR, Type.LONG, StdTypeList.LONG_INT, "ushr-long");
+
+    /** <code>r,x: int :: r = ~x;</code> */
+    public static final Rop NOT_INT =
+        new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int");
+
+    /** <code>r,x: long :: r = ~x;</code> */
+    public static final Rop NOT_LONG =
+        new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long");
+
+    /** <code>r,x,c: int :: r = x + c;</code> */
+    public static final Rop ADD_CONST_INT =
+        new Rop(RegOps.ADD, Type.INT, StdTypeList.INT, "add-const-int");
+
+    /** <code>r,x,c: long :: r = x + c;</code> */
+    public static final Rop ADD_CONST_LONG =
+        new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG, "add-const-long");
+
+    /** <code>r,x,c: float :: r = x + c;</code> */
+    public static final Rop ADD_CONST_FLOAT =
+        new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT, "add-const-float");
+
+    /** <code>r,x,c: double :: r = x + c;</code> */
+    public static final Rop ADD_CONST_DOUBLE =
+        new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE,
+                "add-const-double");
+
+    /** <code>r,x,c: int :: r = x - c;</code> */
+    public static final Rop SUB_CONST_INT =
+        new Rop(RegOps.SUB, Type.INT, StdTypeList.INT, "sub-const-int");
+
+    /** <code>r,x,c: long :: r = x - c;</code> */
+    public static final Rop SUB_CONST_LONG =
+        new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG, "sub-const-long");
+
+    /** <code>r,x,c: float :: r = x - c;</code> */
+    public static final Rop SUB_CONST_FLOAT =
+        new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT, "sub-const-float");
+
+    /** <code>r,x,c: double :: r = x - c;</code> */
+    public static final Rop SUB_CONST_DOUBLE =
+        new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE,
+                "sub-const-double");
+
+    /** <code>r,x,c: int :: r = x * c;</code> */
+    public static final Rop MUL_CONST_INT =
+        new Rop(RegOps.MUL, Type.INT, StdTypeList.INT, "mul-const-int");
+
+    /** <code>r,x,c: long :: r = x * c;</code> */
+    public static final Rop MUL_CONST_LONG =
+        new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG, "mul-const-long");
+
+    /** <code>r,x,c: float :: r = x * c;</code> */
+    public static final Rop MUL_CONST_FLOAT =
+        new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT, "mul-const-float");
+
+    /** <code>r,x,c: double :: r = x * c;</code> */
+    public static final Rop MUL_CONST_DOUBLE =
+        new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE,
+                "mul-const-double");
+
+    /** <code>r,x,c: int :: r = x / c;</code> */
+    public static final Rop DIV_CONST_INT =
+        new Rop(RegOps.DIV, Type.INT, StdTypeList.INT,
+                Exceptions.LIST_Error_ArithmeticException, "div-const-int");
+
+    /** <code>r,x,c: long :: r = x / c;</code> */
+    public static final Rop DIV_CONST_LONG =
+        new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG,
+                Exceptions.LIST_Error_ArithmeticException, "div-const-long");
+
+    /** <code>r,x,c: float :: r = x / c;</code> */
+    public static final Rop DIV_CONST_FLOAT =
+        new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT, "div-const-float");
+
+    /** <code>r,x,c: double :: r = x / c;</code> */
+    public static final Rop DIV_CONST_DOUBLE =
+        new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE,
+                "div-const-double");
+
+    /** <code>r,x,c: int :: r = x % c;</code> */
+    public static final Rop REM_CONST_INT =
+        new Rop(RegOps.REM, Type.INT, StdTypeList.INT,
+                Exceptions.LIST_Error_ArithmeticException, "rem-const-int");
+
+    /** <code>r,x,c: long :: r = x % c;</code> */
+    public static final Rop REM_CONST_LONG =
+        new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG,
+                Exceptions.LIST_Error_ArithmeticException, "rem-const-long");
+
+    /** <code>r,x,c: float :: r = x % c;</code> */
+    public static final Rop REM_CONST_FLOAT =
+        new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT, "rem-const-float");
+
+    /** <code>r,x,c: double :: r = x % c;</code> */
+    public static final Rop REM_CONST_DOUBLE =
+        new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE,
+                "rem-const-double");
+
+    /** <code>r,x,c: int :: r = x &amp; c;</code> */
+    public static final Rop AND_CONST_INT =
+        new Rop(RegOps.AND, Type.INT, StdTypeList.INT, "and-const-int");
+
+    /** <code>r,x,c: long :: r = x &amp; c;</code> */
+    public static final Rop AND_CONST_LONG =
+        new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG, "and-const-long");
+
+    /** <code>r,x,c: int :: r = x | c;</code> */
+    public static final Rop OR_CONST_INT =
+        new Rop(RegOps.OR, Type.INT, StdTypeList.INT, "or-const-int");
+
+    /** <code>r,x,c: long :: r = x | c;</code> */
+    public static final Rop OR_CONST_LONG =
+        new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG, "or-const-long");
+
+    /** <code>r,x,c: int :: r = x ^ c;</code> */
+    public static final Rop XOR_CONST_INT =
+        new Rop(RegOps.XOR, Type.INT, StdTypeList.INT, "xor-const-int");
+
+    /** <code>r,x,c: long :: r = x ^ c;</code> */
+    public static final Rop XOR_CONST_LONG =
+        new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG, "xor-const-long");
+
+    /** <code>r,x,c: int :: r = x &lt;&lt; c;</code> */
+    public static final Rop SHL_CONST_INT =
+        new Rop(RegOps.SHL, Type.INT, StdTypeList.INT, "shl-const-int");
+
+    /** <code>r,x: long; c: int :: r = x &lt;&lt; c;</code> */
+    public static final Rop SHL_CONST_LONG =
+        new Rop(RegOps.SHL, Type.LONG, StdTypeList.INT, "shl-const-long");
+
+    /** <code>r,x,c: int :: r = x &gt;&gt; c;</code> */
+    public static final Rop SHR_CONST_INT =
+        new Rop(RegOps.SHR, Type.INT, StdTypeList.INT, "shr-const-int");
+
+    /** <code>r,x: long; c: int :: r = x &gt;&gt; c;</code> */
+    public static final Rop SHR_CONST_LONG =
+        new Rop(RegOps.SHR, Type.LONG, StdTypeList.INT, "shr-const-long");
+
+    /** <code>r,x,c: int :: r = x &gt;&gt;&gt; c;</code> */
+    public static final Rop USHR_CONST_INT =
+        new Rop(RegOps.USHR, Type.INT, StdTypeList.INT, "ushr-const-int");
+
+    /** <code>r,x: long; c: int :: r = x &gt;&gt;&gt; c;</code> */
+    public static final Rop USHR_CONST_LONG =
+        new Rop(RegOps.USHR, Type.LONG, StdTypeList.INT, "ushr-const-long");
+
+    /** <code>r: int; x,y: long :: r = cmp(x, y);</code> */
+    public static final Rop CMPL_LONG =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.LONG_LONG, "cmpl-long");
+
+    /** <code>r: int; x,y: float :: r = cmpl(x, y);</code> */
+    public static final Rop CMPL_FLOAT =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpl-float");
+
+    /** <code>r: int; x,y: double :: r = cmpl(x, y);</code> */
+    public static final Rop CMPL_DOUBLE =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+                "cmpl-double");
+
+    /** <code>r: int; x,y: float :: r = cmpg(x, y);</code> */
+    public static final Rop CMPG_FLOAT =
+        new Rop(RegOps.CMPG, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpg-float");
+
+    /** <code>r: int; x,y: double :: r = cmpg(x, y);</code> */
+    public static final Rop CMPG_DOUBLE =
+        new Rop(RegOps.CMPG, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+                "cmpg-double");
+
+    /** <code>r: int; x: long :: r = (int) x</code> */
+    public static final Rop CONV_L2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i");
+
+    /** <code>r: int; x: float :: r = (int) x</code> */
+    public static final Rop CONV_F2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i");
+
+    /** <code>r: int; x: double :: r = (int) x</code> */
+    public static final Rop CONV_D2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i");
+
+    /** <code>r: long; x: int :: r = (long) x</code> */
+    public static final Rop CONV_I2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l");
+
+    /** <code>r: long; x: float :: r = (long) x</code> */
+    public static final Rop CONV_F2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l");
+
+    /** <code>r: long; x: double :: r = (long) x</code> */
+    public static final Rop CONV_D2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l");
+
+    /** <code>r: float; x: int :: r = (float) x</code> */
+    public static final Rop CONV_I2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f");
+
+    /** <code>r: float; x: long :: r = (float) x</code> */
+    public static final Rop CONV_L2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f");
+
+    /** <code>r: float; x: double :: r = (float) x</code> */
+    public static final Rop CONV_D2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f");
+
+    /** <code>r: double; x: int :: r = (double) x</code> */
+    public static final Rop CONV_I2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d");
+
+    /** <code>r: double; x: long :: r = (double) x</code> */
+    public static final Rop CONV_L2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d");
+
+    /** <code>r: double; x: float :: r = (double) x</code> */
+    public static final Rop CONV_F2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d");
+
+    /**
+     * <code>r,x: int :: r = (x &lt;&lt; 24) &gt;&gt; 24</code> (Java-style
+     * convert int to byte) 
+     */
+    public static final Rop TO_BYTE = 
+        new Rop(RegOps.TO_BYTE, Type.INT, StdTypeList.INT, "to-byte");
+
+    /**
+     * <code>r,x: int :: r = x &amp; 0xffff</code> (Java-style
+     * convert int to char) 
+     */
+    public static final Rop TO_CHAR =
+        new Rop(RegOps.TO_CHAR, Type.INT, StdTypeList.INT, "to-char");
+
+    /**
+     * <code>r,x: int :: r = (x &lt;&lt; 16) &gt;&gt; 16</code> (Java-style
+     * convert int to short) 
+     */
+    public static final Rop TO_SHORT =
+        new Rop(RegOps.TO_SHORT, Type.INT, StdTypeList.INT, "to-short");
+
+    /** <code>return void</code> */
+    public static final Rop RETURN_VOID =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN,
+                "return-void");
+
+    /** <code>x: int; return x</code> */
+    public static final Rop RETURN_INT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN,
+                "return-int");
+
+    /** <code>x: long; return x</code> */
+    public static final Rop RETURN_LONG =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN,
+                "return-long");
+
+    /** <code>x: float; return x</code> */
+    public static final Rop RETURN_FLOAT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN,
+                "return-float");
+
+    /** <code>x: double; return x</code> */
+    public static final Rop RETURN_DOUBLE =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE,
+                Rop.BRANCH_RETURN, "return-double");
+
+    /** <code>x: Object; return x</code> */
+    public static final Rop RETURN_OBJECT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT,
+                Rop.BRANCH_RETURN, "return-object");
+
+    /** <code>T: any type; r: int; x: T[]; :: r = x.length</code> */
+    public static final Rop ARRAY_LENGTH =
+        new Rop(RegOps.ARRAY_LENGTH, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "array-length");
+
+    /** <code>x: Throwable :: throw(x)</code> */
+    public static final Rop THROW =
+        new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE,
+                StdTypeList.THROWABLE, "throw");
+
+    /** <code>x: Object :: monitorenter(x)</code> */
+    public static final Rop MONITOR_ENTER =
+        new Rop(RegOps.MONITOR_ENTER, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "monitor-enter");
+
+    /** <code>x: Object :: monitorexit(x)</code> */
+    public static final Rop MONITOR_EXIT =
+        new Rop(RegOps.MONITOR_EXIT, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_Null_IllegalMonitorStateException,
+                "monitor-exit");
+
+    /** <code>r,y: int; x: int[] :: r = x[y]</code> */
+    public static final Rop AGET_INT = 
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.INTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-int");
+
+    /** <code>r: long; x: long[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_LONG = 
+        new Rop(RegOps.AGET, Type.LONG, StdTypeList.LONGARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-long");
+
+    /** <code>r: float; x: float[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_FLOAT = 
+        new Rop(RegOps.AGET, Type.FLOAT, StdTypeList.FLOATARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-float");
+
+    /** <code>r: double; x: double[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_DOUBLE = 
+        new Rop(RegOps.AGET, Type.DOUBLE, StdTypeList.DOUBLEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-double");
+
+    /** <code>r: Object; x: Object[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_OBJECT = 
+        new Rop(RegOps.AGET, Type.OBJECT, StdTypeList.OBJECTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-object");
+
+    /** <code>r: boolean; x: boolean[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_BOOLEAN = 
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.BOOLEANARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-boolean");
+
+    /** <code>r: byte; x: byte[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_BYTE = 
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.BYTEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-byte");
+
+    /** <code>r: char; x: char[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_CHAR = 
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.CHARARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-char");
+
+    /** <code>r: short; x: short[]; y: int :: r = x[y]</code> */
+    public static final Rop AGET_SHORT = 
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.SHORTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-short");
+
+    /** <code>x,z: int; y: int[] :: y[z] = x</code> */
+    public static final Rop APUT_INT = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_INTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-int");
+
+    /** <code>x: long; y: long[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_LONG = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.LONG_LONGARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-long");
+
+    /** <code>x: float; y: float[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_FLOAT = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.FLOAT_FLOATARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aput-float");
+
+    /** <code>x: double; y: double[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_DOUBLE = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.DOUBLE_DOUBLEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aput-double");
+
+    /** <code>x: Object; y: Object[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_OBJECT = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.OBJECT_OBJECTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-object");
+
+    /** <code>x: boolean; y: boolean[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_BOOLEAN = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BOOLEANARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-boolean");
+
+    /** <code>x: byte; y: byte[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_BYTE = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BYTEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-byte");
+
+    /** <code>x: char; y: char[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_CHAR = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_CHARARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-char");
+
+    /** <code>x: short; y: short[]; z: int :: y[z] = x</code> */
+    public static final Rop APUT_SHORT = 
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_SHORTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-short");
+
+    /**
+     * <code>T: any non-array object type :: r =
+     * alloc(T)</code> (allocate heap space for an object) 
+     */
+    public static final Rop NEW_INSTANCE =
+        new Rop(RegOps.NEW_INSTANCE, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "new-instance");
+
+    /** <code>r: int[]; x: int :: r = new int[x]</code> */
+    public static final Rop NEW_ARRAY_INT =
+        new Rop(RegOps.NEW_ARRAY, Type.INT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-int");
+
+    /** <code>r: long[]; x: int :: r = new long[x]</code> */
+    public static final Rop NEW_ARRAY_LONG =
+        new Rop(RegOps.NEW_ARRAY, Type.LONG_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-long");
+
+    /** <code>r: float[]; x: int :: r = new float[x]</code> */
+    public static final Rop NEW_ARRAY_FLOAT =
+        new Rop(RegOps.NEW_ARRAY, Type.FLOAT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-float");
+
+    /** <code>r: double[]; x: int :: r = new double[x]</code> */
+    public static final Rop NEW_ARRAY_DOUBLE =
+        new Rop(RegOps.NEW_ARRAY, Type.DOUBLE_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-double");
+
+    /** <code>r: boolean[]; x: int :: r = new boolean[x]</code> */
+    public static final Rop NEW_ARRAY_BOOLEAN =
+        new Rop(RegOps.NEW_ARRAY, Type.BOOLEAN_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-boolean");
+
+    /** <code>r: byte[]; x: int :: r = new byte[x]</code> */
+    public static final Rop NEW_ARRAY_BYTE =
+        new Rop(RegOps.NEW_ARRAY, Type.BYTE_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-byte");
+
+    /** <code>r: char[]; x: int :: r = new char[x]</code> */
+    public static final Rop NEW_ARRAY_CHAR =
+        new Rop(RegOps.NEW_ARRAY, Type.CHAR_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-char");
+
+    /** <code>r: short[]; x: int :: r = new short[x]</code> */
+    public static final Rop NEW_ARRAY_SHORT =
+        new Rop(RegOps.NEW_ARRAY, Type.SHORT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-short");
+
+    /**
+     * <code>T: any non-array object type; x: Object :: (T) x</code> (can
+     * throw <code>ClassCastException</code>) 
+     */
+    public static final Rop CHECK_CAST = 
+        new Rop(RegOps.CHECK_CAST, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_ClassCastException, "check-cast");
+
+    /**
+     * <code>T: any non-array object type; x: Object :: x instanceof
+     * T</code>. Note: This is listed as throwing <code>Error</code>
+     * explicitly because the op <i>can</i> throw, but there are no
+     * other predefined exceptions for it. 
+     */
+    public static final Rop INSTANCE_OF =
+        new Rop(RegOps.INSTANCE_OF, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error, "instance-of");
+
+    /**
+     * <code>r: int; x: Object; f: instance field spec of
+     * type int :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_INT =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "get-field-int");
+
+    /**
+     * <code>r: long; x: Object; f: instance field spec of
+     * type long :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_LONG =
+        new Rop(RegOps.GET_FIELD, Type.LONG, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "get-field-long");
+
+    /**
+     * <code>r: float; x: Object; f: instance field spec of
+     * type float :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_FLOAT =
+        new Rop(RegOps.GET_FIELD, Type.FLOAT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-float");
+
+    /**
+     * <code>r: double; x: Object; f: instance field spec of
+     * type double :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_DOUBLE =
+        new Rop(RegOps.GET_FIELD, Type.DOUBLE, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-double");
+
+    /**
+     * <code>r: Object; x: Object; f: instance field spec of
+     * type Object :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_OBJECT =
+        new Rop(RegOps.GET_FIELD, Type.OBJECT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-object");
+
+    /**
+     * <code>r: boolean; x: Object; f: instance field spec of
+     * type boolean :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_BOOLEAN =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-boolean");
+
+    /**
+     * <code>r: byte; x: Object; f: instance field spec of
+     * type byte :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_BYTE =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-byte");
+
+    /**
+     * <code>r: char; x: Object; f: instance field spec of
+     * type char :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_CHAR =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-char");
+
+    /**
+     * <code>r: short; x: Object; f: instance field spec of
+     * type short :: r = x.f</code> 
+     */
+    public static final Rop GET_FIELD_SHORT =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-short");
+
+    /**
+     * <code>r: int; f: static field spec of type int :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_INT =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-int");
+
+    /**
+     * <code>r: long; f: static field spec of type long :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_LONG =
+        new Rop(RegOps.GET_STATIC, Type.LONG, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-long");
+
+    /**
+     * <code>r: float; f: static field spec of type float :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_FLOAT =
+        new Rop(RegOps.GET_STATIC, Type.FLOAT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-float");
+
+    /**
+     * <code>r: double; f: static field spec of type double :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_DOUBLE =
+        new Rop(RegOps.GET_STATIC, Type.DOUBLE, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-double");
+
+    /**
+     * <code>r: Object; f: static field spec of type Object :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_OBJECT =
+        new Rop(RegOps.GET_STATIC, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-object");
+
+    /**
+     * <code>r: boolean; f: static field spec of type boolean :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_BOOLEAN =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-boolean");
+
+    /**
+     * <code>r: byte; f: static field spec of type byte :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_BYTE =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-byte");
+
+    /**
+     * <code>r: char; f: static field spec of type char :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_CHAR =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-char");
+
+    /**
+     * <code>r: short; f: static field spec of type short :: r =
+     * f</code> 
+     */
+    public static final Rop GET_STATIC_SHORT =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-short");
+
+    /**
+     * <code>x: int; y: Object; f: instance field spec of type
+     * int :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_INT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "put-field-int");
+
+    /**
+     * <code>x: long; y: Object; f: instance field spec of type
+     * long :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_LONG =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.LONG_OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "put-field-long");
+
+    /**
+     * <code>x: float; y: Object; f: instance field spec of type
+     * float :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_FLOAT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.FLOAT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-float");
+
+    /**
+     * <code>x: double; y: Object; f: instance field spec of type
+     * double :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_DOUBLE =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.DOUBLE_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-double");
+
+    /**
+     * <code>x: Object; y: Object; f: instance field spec of type
+     * Object :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_OBJECT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-object");
+
+    /**
+     * <code>x: int; y: Object; f: instance field spec of type
+     * boolean :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_BOOLEAN =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-boolean");
+
+    /**
+     * <code>x: int; y: Object; f: instance field spec of type
+     * byte :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_BYTE =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-byte");
+
+    /**
+     * <code>x: int; y: Object; f: instance field spec of type
+     * char :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_CHAR =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-char");
+
+    /**
+     * <code>x: int; y: Object; f: instance field spec of type
+     * short :: y.f = x</code> 
+     */
+    public static final Rop PUT_FIELD_SHORT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-short");
+
+    /**
+     * <code>f: static field spec of type int; x: int :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_INT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-int");
+
+    /**
+     * <code>f: static field spec of type long; x: long :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_LONG =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.LONG,
+                Exceptions.LIST_Error, "put-static-long");
+
+    /**
+     * <code>f: static field spec of type float; x: float :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_FLOAT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.FLOAT,
+                Exceptions.LIST_Error, "put-static-float");
+
+    /**
+     * <code>f: static field spec of type double; x: double :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_DOUBLE =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.DOUBLE,
+                Exceptions.LIST_Error, "put-static-double");
+
+    /**
+     * <code>f: static field spec of type Object; x: Object :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_OBJECT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error, "put-static-object");
+
+    /**
+     * <code>f: static field spec of type boolean; x: boolean :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_BOOLEAN =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-boolean");
+
+    /**
+     * <code>f: static field spec of type byte; x: byte :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_BYTE =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-byte");
+
+    /**
+     * <code>f: static field spec of type char; x: char :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_CHAR =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-char");
+
+    /**
+     * <code>f: static field spec of type short; x: short :: f =
+     * x</code>
+     */
+    public static final Rop PUT_STATIC_SHORT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-short");
+
+    /** <code>x: Int :: local variable begins in x */
+    public static final Rop MARK_LOCAL_INT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.INT, "mark-local-int");
+
+    /** <code>x: Long :: local variable begins in x */
+    public static final Rop MARK_LOCAL_LONG =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.LONG, "mark-local-long");
+
+    /** <code>x: Float :: local variable begins in x */
+    public static final Rop MARK_LOCAL_FLOAT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.FLOAT, "mark-local-float");
+
+    /** <code>x: Double :: local variable begins in x */
+    public static final Rop MARK_LOCAL_DOUBLE =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.DOUBLE, "mark-local-double");
+
+    /** <code>x: Object :: local variable begins in x */
+    public static final Rop MARK_LOCAL_OBJECT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.OBJECT, "mark-local-object");
+
+    /** <code>T: Any primitive type; v0..vx: T :: {v0, ..., vx}</code> */
+    public static final Rop FILL_ARRAY_DATA =
+        new Rop(RegOps.FILL_ARRAY_DATA, Type.VOID, StdTypeList.EMPTY,
+                "fill-array-data");
+
+    /**
+     * Returns the appropriate rop for the given opcode, destination,
+     * and sources. The result is typically, but not necessarily, a
+     * shared instance.
+     * 
+     * <p><b>Note:</b> This method does not do complete error checking on
+     * its arguments, and so it may return an instance which seemed "right
+     * enough" even though in actuality the passed arguments don't quite
+     * match what is returned. TODO: Revisit this issue.</p>
+     * 
+     * @param opcode the opcode
+     * @param dest non-null; destination type, or {@link Type#VOID} if none
+     * @param sources non-null; list of source types
+     * @param cst null-ok; associated constant, if any
+     * @return non-null; an appropriate instance
+     */
+    public static Rop ropFor(int opcode, TypeBearer dest, TypeList sources,
+                             Constant cst) {
+        switch (opcode) {
+            case RegOps.NOP: return NOP;
+            case RegOps.MOVE: return opMove(dest);
+            case RegOps.MOVE_PARAM: return opMoveParam(dest);
+            case RegOps.MOVE_EXCEPTION: return opMoveException(dest);
+            case RegOps.CONST: return opConst(dest);
+            case RegOps.GOTO: return GOTO;
+            case RegOps.IF_EQ: return opIfEq(sources);
+            case RegOps.IF_NE: return opIfNe(sources);
+            case RegOps.IF_LT: return opIfLt(sources);
+            case RegOps.IF_GE: return opIfGe(sources);
+            case RegOps.IF_LE: return opIfLe(sources);
+            case RegOps.IF_GT: return opIfGt(sources);
+            case RegOps.SWITCH: return SWITCH;
+            case RegOps.ADD: return opAdd(sources);
+            case RegOps.SUB: return opSub(sources);
+            case RegOps.MUL: return opMul(sources);
+            case RegOps.DIV: return opDiv(sources);
+            case RegOps.REM: return opRem(sources);
+            case RegOps.NEG: return opNeg(dest);
+            case RegOps.AND: return opAnd(sources);
+            case RegOps.OR: return opOr(sources);
+            case RegOps.XOR: return opXor(sources);
+            case RegOps.SHL: return opShl(sources);
+            case RegOps.SHR: return opShr(sources);
+            case RegOps.USHR: return opUshr(sources);
+            case RegOps.NOT: return opNot(dest);
+            case RegOps.CMPL: return opCmpl(sources.getType(0));
+            case RegOps.CMPG: return opCmpg(sources.getType(0));
+            case RegOps.CONV: return opConv(dest, sources.getType(0));
+            case RegOps.TO_BYTE: return TO_BYTE;
+            case RegOps.TO_CHAR: return TO_CHAR;
+            case RegOps.TO_SHORT: return TO_SHORT;
+            case RegOps.RETURN: {
+                if (sources.size() == 0) {
+                    return RETURN_VOID;
+                }
+                return opReturn(sources.getType(0));
+            }
+            case RegOps.ARRAY_LENGTH: return ARRAY_LENGTH;
+            case RegOps.THROW: return THROW;
+            case RegOps.MONITOR_ENTER: return MONITOR_ENTER;
+            case RegOps.MONITOR_EXIT: return MONITOR_EXIT;
+            case RegOps.AGET: {
+                Type source = sources.getType(0);
+                if (source == Type.KNOWN_NULL) {
+                    // Treat a known-null as an Object[] in this context.
+                    source = Type.OBJECT_ARRAY;
+                } 
+                return opAget(source.getComponentType());
+            }
+            case RegOps.APUT: {
+                Type source = sources.getType(1);
+                if (source == Type.KNOWN_NULL) {
+                    // Treat a known-null as an Object[] in this context.
+                    source = Type.OBJECT_ARRAY;
+                } 
+                return opAput(source.getComponentType());
+            }
+            case RegOps.NEW_INSTANCE: return NEW_INSTANCE;
+            case RegOps.NEW_ARRAY: return opNewArray(dest.getType());
+            case RegOps.CHECK_CAST: return CHECK_CAST;
+            case RegOps.INSTANCE_OF: return INSTANCE_OF;
+            case RegOps.GET_FIELD: return opGetField(dest);
+            case RegOps.GET_STATIC: return opGetStatic(dest);
+            case RegOps.PUT_FIELD: return opPutField(sources.getType(0));
+            case RegOps.PUT_STATIC: return opPutStatic(sources.getType(0));
+            case RegOps.INVOKE_STATIC: {
+                return opInvokeStatic(((CstMethodRef) cst).getPrototype());
+            }
+            case RegOps.INVOKE_VIRTUAL: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeVirtual(meth);
+            }
+            case RegOps.INVOKE_SUPER: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeSuper(meth);
+            }
+            case RegOps.INVOKE_DIRECT: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeDirect(meth);
+            }
+            case RegOps.INVOKE_INTERFACE: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeInterface(meth);
+            }
+        }
+
+        throw new RuntimeException("unknown opcode " + RegOps.opName(opcode));
+    }
+
+    /**
+     * Returns the appropriate <code>move</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being moved
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMove(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MOVE_INT;
+            case Type.BT_LONG:   return MOVE_LONG;
+            case Type.BT_FLOAT:  return MOVE_FLOAT;
+            case Type.BT_DOUBLE: return MOVE_DOUBLE;
+            case Type.BT_OBJECT: return MOVE_OBJECT;
+            case Type.BT_ADDR:   return MOVE_RETURN_ADDRESS;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>move-param</code> rop for the
+     * given type. The result is a shared instance.
+     * 
+     * @param type non-null; type of value being moved
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMoveParam(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MOVE_PARAM_INT;
+            case Type.BT_LONG:   return MOVE_PARAM_LONG;
+            case Type.BT_FLOAT:  return MOVE_PARAM_FLOAT;
+            case Type.BT_DOUBLE: return MOVE_PARAM_DOUBLE;
+            case Type.BT_OBJECT: return MOVE_PARAM_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>move-exception</code> rop for the
+     * given type. The result may be a shared instance.
+     * 
+     * @param type non-null; type of the exception
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMoveException(TypeBearer type) {
+        return new Rop(RegOps.MOVE_EXCEPTION, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate <code>move-result</code> rop for the
+     * given type. The result may be a shared instance.
+     *
+     * @param type non-null; type of the parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMoveResult(TypeBearer type) {
+        return new Rop(RegOps.MOVE_RESULT, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate <code>move-result-pseudo</code> rop for the
+     * given type. The result may be a shared instance.
+     *
+     * @param type non-null; type of the parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMoveResultPseudo(TypeBearer type) {
+        return new Rop(RegOps.MOVE_RESULT_PSEUDO, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate <code>const</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param type non-null; type of the constant
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opConst(TypeBearer type) {
+        if (type.getType() == Type.KNOWN_NULL) {
+            return CONST_OBJECT_NOTHROW;
+        }
+
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return CONST_INT;
+            case Type.BT_LONG:   return CONST_LONG;
+            case Type.BT_FLOAT:  return CONST_FLOAT;
+            case Type.BT_DOUBLE: return CONST_DOUBLE;
+            case Type.BT_OBJECT: return CONST_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>if-eq</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfEq(TypeList types) {
+        return pickIf(types, IF_EQZ_INT, IF_EQZ_OBJECT,
+                      IF_EQ_INT, IF_EQ_OBJECT);
+    }
+
+    /**
+     * Returns the appropriate <code>if-ne</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfNe(TypeList types) {
+        return pickIf(types, IF_NEZ_INT, IF_NEZ_OBJECT,
+                      IF_NE_INT, IF_NE_OBJECT);
+    }
+
+    /**
+     * Returns the appropriate <code>if-lt</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfLt(TypeList types) {
+        return pickIf(types, IF_LTZ_INT, null, IF_LT_INT, null);
+    }
+
+    /**
+     * Returns the appropriate <code>if-ge</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfGe(TypeList types) {
+        return pickIf(types, IF_GEZ_INT, null, IF_GE_INT, null);
+    }
+
+    /**
+     * Returns the appropriate <code>if-gt</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfGt(TypeList types) {
+        return pickIf(types, IF_GTZ_INT, null, IF_GT_INT, null);
+    }
+
+    /**
+     * Returns the appropriate <code>if-le</code> rop for the given
+     * sources. The result is a shared instance.
+     * 
+     * @param types non-null; source types
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opIfLe(TypeList types) {
+        return pickIf(types, IF_LEZ_INT, null, IF_LE_INT, null);
+    }
+
+    /**
+     * Helper for all the <code>if*</code>-related methods, which
+     * checks types and picks one of the four variants, throwing if
+     * there's a problem.
+     * 
+     * @param types non-null; the types
+     * @param intZ non-null; the int-to-0 comparison
+     * @param objZ null-ok; the object-to-null comparison
+     * @param intInt non-null; the int-to-int comparison
+     * @param objObj non-null; the object-to-object comparison
+     * @return non-null; the appropriate instance
+     */
+    private static Rop pickIf(TypeList types, Rop intZ, Rop objZ, Rop intInt,
+                              Rop objObj) {
+        switch(types.size()) {
+            case 1: {
+                switch (types.getType(0).getBasicFrameType()) {
+                    case Type.BT_INT: {
+                        return intZ;
+                    }
+                    case Type.BT_OBJECT: {
+                        if (objZ != null) {
+                            return objZ;
+                        }
+                    }
+                }
+                break;
+            }
+            case 2: {
+                int bt = types.getType(0).getBasicFrameType();
+                if (bt == types.getType(1).getBasicFrameType()) {
+                    switch (bt) {
+                        case Type.BT_INT: {
+                            return intInt;
+                        }
+                        case Type.BT_OBJECT: {
+                            if (objObj != null) {
+                                return objObj;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+
+        return throwBadTypes(types);
+    }
+
+    /**
+     * Returns the appropriate <code>add</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opAdd(TypeList types) {
+        return pickBinaryOp(types, ADD_CONST_INT, ADD_CONST_LONG,
+                            ADD_CONST_FLOAT, ADD_CONST_DOUBLE, ADD_INT,
+                            ADD_LONG, ADD_FLOAT, ADD_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate <code>sub</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opSub(TypeList types) {
+        return pickBinaryOp(types, SUB_CONST_INT, SUB_CONST_LONG,
+                            SUB_CONST_FLOAT, SUB_CONST_DOUBLE, SUB_INT,
+                            SUB_LONG, SUB_FLOAT, SUB_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate <code>mul</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMul(TypeList types) {
+        return pickBinaryOp(types, MUL_CONST_INT, MUL_CONST_LONG,
+                            MUL_CONST_FLOAT, MUL_CONST_DOUBLE, MUL_INT,
+                            MUL_LONG, MUL_FLOAT, MUL_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate <code>div</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opDiv(TypeList types) {
+        return pickBinaryOp(types, DIV_CONST_INT, DIV_CONST_LONG,
+                            DIV_CONST_FLOAT, DIV_CONST_DOUBLE, DIV_INT,
+                            DIV_LONG, DIV_FLOAT, DIV_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate <code>rem</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opRem(TypeList types) {
+        return pickBinaryOp(types, REM_CONST_INT, REM_CONST_LONG,
+                            REM_CONST_FLOAT, REM_CONST_DOUBLE, REM_INT,
+                            REM_LONG, REM_FLOAT, REM_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate <code>and</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opAnd(TypeList types) {
+        return pickBinaryOp(types, AND_CONST_INT, AND_CONST_LONG, null, null,
+                            AND_INT, AND_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate <code>or</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opOr(TypeList types) {
+        return pickBinaryOp(types, OR_CONST_INT, OR_CONST_LONG, null, null,
+                            OR_INT, OR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate <code>xor</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opXor(TypeList types) {
+        return pickBinaryOp(types, XOR_CONST_INT, XOR_CONST_LONG, null, null,
+                            XOR_INT, XOR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate <code>shl</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opShl(TypeList types) {
+        return pickBinaryOp(types, SHL_CONST_INT, SHL_CONST_LONG, null, null,
+                            SHL_INT, SHL_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate <code>shr</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opShr(TypeList types) {
+        return pickBinaryOp(types, SHR_CONST_INT, SHR_CONST_LONG, null, null,
+                            SHR_INT, SHR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate <code>ushr</code> rop for the given
+     * types. The result is a shared instance.
+     * 
+     * @param types non-null; types of the sources
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opUshr(TypeList types) {
+        return pickBinaryOp(types, USHR_CONST_INT, USHR_CONST_LONG, null, null,
+                            USHR_INT, USHR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate binary arithmetic rop for the given type
+     * and arguments. The result is a shared instance.
+     * 
+     * @param types non-null; sources of the operation
+     * @param int1 non-null; the int-to-constant rop
+     * @param long1 non-null; the long-to-constant rop
+     * @param float1 null-ok; the float-to-constant rop, if any
+     * @param double1 null-ok; the double-to-constant rop, if any
+     * @param int2 non-null; the int-to-int rop
+     * @param long2 non-null; the long-to-long or long-to-int rop
+     * @param float2 null-ok; the float-to-float rop, if any
+     * @param double2 null-ok; the double-to-double rop, if any
+     * @return non-null; an appropriate instance
+     */
+    private static Rop pickBinaryOp(TypeList types, Rop int1, Rop long1,
+                                    Rop float1, Rop double1, Rop int2,
+                                    Rop long2, Rop float2, Rop double2) {
+        int bt1 = types.getType(0).getBasicFrameType();
+        Rop result = null;
+
+        switch (types.size()) {
+            case 1: {
+                switch(bt1) {
+                    case Type.BT_INT:    return int1;
+                    case Type.BT_LONG:   return long1;
+                    case Type.BT_FLOAT:  result = float1; break;
+                    case Type.BT_DOUBLE: result = double1; break;
+                }
+                break;
+            }
+            case 2: {
+                switch(bt1) {
+                    case Type.BT_INT:    return int2;
+                    case Type.BT_LONG:   return long2;
+                    case Type.BT_FLOAT:  result = float2; break;
+                    case Type.BT_DOUBLE: result = double2; break;
+                }
+                break;
+            }
+        }
+
+        if (result == null) {
+            return throwBadTypes(types);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the appropriate <code>neg</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being operated on
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opNeg(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return NEG_INT;
+            case Type.BT_LONG:   return NEG_LONG;
+            case Type.BT_FLOAT:  return NEG_FLOAT;
+            case Type.BT_DOUBLE: return NEG_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>not</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being operated on
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opNot(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:  return NOT_INT;
+            case Type.BT_LONG: return NOT_LONG;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>cmpl</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being compared
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opCmpl(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_LONG:   return CMPL_LONG;
+            case Type.BT_FLOAT:  return CMPL_FLOAT;
+            case Type.BT_DOUBLE: return CMPL_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>cmpg</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being compared
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opCmpg(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_FLOAT:  return CMPG_FLOAT;
+            case Type.BT_DOUBLE: return CMPG_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>conv</code> rop for the given types. The
+     * result is a shared instance.
+     * 
+     * @param dest non-null; target value type
+     * @param source non-null; source value type
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opConv(TypeBearer dest, TypeBearer source) {
+        int dbt = dest.getBasicFrameType();
+        switch (source.getBasicFrameType()) {
+            case Type.BT_INT: {
+                switch (dbt) {
+                    case Type.BT_LONG:   return CONV_I2L;
+                    case Type.BT_FLOAT:  return CONV_I2F;
+                    case Type.BT_DOUBLE: return CONV_I2D;
+                }
+            }
+            case Type.BT_LONG: {
+                switch (dbt) {
+                    case Type.BT_INT:    return CONV_L2I;
+                    case Type.BT_FLOAT:  return CONV_L2F;
+                    case Type.BT_DOUBLE: return CONV_L2D;
+                }
+            }
+            case Type.BT_FLOAT: {
+                switch (dbt) {
+                    case Type.BT_INT:    return CONV_F2I;
+                    case Type.BT_LONG:   return CONV_F2L;
+                    case Type.BT_DOUBLE: return CONV_F2D;
+                }
+            }
+            case Type.BT_DOUBLE: {
+                switch (dbt) {
+                    case Type.BT_INT:   return CONV_D2I;
+                    case Type.BT_LONG:  return CONV_D2L;
+                    case Type.BT_FLOAT: return CONV_D2F;
+                }
+            }
+        }
+
+        return throwBadTypes(StdTypeList.make(dest.getType(),
+                                              source.getType()));
+    }
+
+    /**
+     * Returns the appropriate <code>return</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; type of value being returned
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opReturn(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return RETURN_INT;
+            case Type.BT_LONG:   return RETURN_LONG;
+            case Type.BT_FLOAT:  return RETURN_FLOAT;
+            case Type.BT_DOUBLE: return RETURN_DOUBLE;
+            case Type.BT_OBJECT: return RETURN_OBJECT;
+            case Type.BT_VOID:   return RETURN_VOID;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>aget</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; element type of array being accessed
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opAget(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return AGET_INT;
+            case Type.BT_LONG:    return AGET_LONG;
+            case Type.BT_FLOAT:   return AGET_FLOAT;
+            case Type.BT_DOUBLE:  return AGET_DOUBLE;
+            case Type.BT_OBJECT:  return AGET_OBJECT;
+            case Type.BT_BOOLEAN: return AGET_BOOLEAN;
+            case Type.BT_BYTE:    return AGET_BYTE;
+            case Type.BT_CHAR:    return AGET_CHAR;
+            case Type.BT_SHORT:   return AGET_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>aput</code> rop for the given type. The
+     * result is a shared instance.
+     * 
+     * @param type non-null; element type of array being accessed
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opAput(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return APUT_INT;
+            case Type.BT_LONG:    return APUT_LONG;
+            case Type.BT_FLOAT:   return APUT_FLOAT;
+            case Type.BT_DOUBLE:  return APUT_DOUBLE;
+            case Type.BT_OBJECT:  return APUT_OBJECT;
+            case Type.BT_BOOLEAN: return APUT_BOOLEAN;
+            case Type.BT_BYTE:    return APUT_BYTE;
+            case Type.BT_CHAR:    return APUT_CHAR;
+            case Type.BT_SHORT:   return APUT_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>new-array</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param arrayType non-null; array type of array being created
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opNewArray(TypeBearer arrayType) {
+        Type type = arrayType.getType();
+        Type elementType = type.getComponentType();
+
+        switch (elementType.getBasicType()) {
+            case Type.BT_INT:     return NEW_ARRAY_INT;
+            case Type.BT_LONG:    return NEW_ARRAY_LONG;
+            case Type.BT_FLOAT:   return NEW_ARRAY_FLOAT;
+            case Type.BT_DOUBLE:  return NEW_ARRAY_DOUBLE;
+            case Type.BT_BOOLEAN: return NEW_ARRAY_BOOLEAN;
+            case Type.BT_BYTE:    return NEW_ARRAY_BYTE;
+            case Type.BT_CHAR:    return NEW_ARRAY_CHAR;
+            case Type.BT_SHORT:   return NEW_ARRAY_SHORT;
+            case Type.BT_OBJECT: {
+                return new Rop(RegOps.NEW_ARRAY, type, StdTypeList.INT,
+                        Exceptions.LIST_Error_NegativeArraySizeException,
+                        "new-array-object");
+            }
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>filled-new-array</code> rop for the given
+     * type. The result may be a shared instance.
+     * 
+     * @param arrayType non-null; type of array being created
+     * @param count &gt;= 0; number of elements that the array should have
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opFilledNewArray(TypeBearer arrayType, int count) {
+        Type type = arrayType.getType();
+        Type elementType = type.getComponentType();
+
+        if (elementType.isCategory2()) {
+            return throwBadType(arrayType);
+        }
+
+        if (count < 0) {
+            throw new IllegalArgumentException("count < 0");
+        }
+
+        StdTypeList sourceTypes = new StdTypeList(count);
+
+        for (int i = 0; i < count; i++) {
+            sourceTypes.set(i, elementType);
+        }
+
+        // Note: The resulting rop is considered call-like.
+        return new Rop(RegOps.FILLED_NEW_ARRAY,
+                       sourceTypes,
+                       Exceptions.LIST_Error);
+    }
+
+    /**
+     * Returns the appropriate <code>get-field</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param type non-null; type of the field in question
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opGetField(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return GET_FIELD_INT;
+            case Type.BT_LONG:    return GET_FIELD_LONG;
+            case Type.BT_FLOAT:   return GET_FIELD_FLOAT;
+            case Type.BT_DOUBLE:  return GET_FIELD_DOUBLE;
+            case Type.BT_OBJECT:  return GET_FIELD_OBJECT;
+            case Type.BT_BOOLEAN: return GET_FIELD_BOOLEAN;
+            case Type.BT_BYTE:    return GET_FIELD_BYTE;
+            case Type.BT_CHAR:    return GET_FIELD_CHAR;
+            case Type.BT_SHORT:   return GET_FIELD_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>put-field</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param type non-null; type of the field in question
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opPutField(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return PUT_FIELD_INT;
+            case Type.BT_LONG:    return PUT_FIELD_LONG;
+            case Type.BT_FLOAT:   return PUT_FIELD_FLOAT;
+            case Type.BT_DOUBLE:  return PUT_FIELD_DOUBLE;
+            case Type.BT_OBJECT:  return PUT_FIELD_OBJECT;
+            case Type.BT_BOOLEAN: return PUT_FIELD_BOOLEAN;
+            case Type.BT_BYTE:    return PUT_FIELD_BYTE;
+            case Type.BT_CHAR:    return PUT_FIELD_CHAR;
+            case Type.BT_SHORT:   return PUT_FIELD_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>get-static</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param type non-null; type of the field in question
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opGetStatic(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return GET_STATIC_INT;
+            case Type.BT_LONG:    return GET_STATIC_LONG;
+            case Type.BT_FLOAT:   return GET_STATIC_FLOAT;
+            case Type.BT_DOUBLE:  return GET_STATIC_DOUBLE;
+            case Type.BT_OBJECT:  return GET_STATIC_OBJECT;
+            case Type.BT_BOOLEAN: return GET_STATIC_BOOLEAN;
+            case Type.BT_BYTE:    return GET_STATIC_BYTE;
+            case Type.BT_CHAR:    return GET_STATIC_CHAR;
+            case Type.BT_SHORT:   return GET_STATIC_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>put-static</code> rop for the given
+     * type. The result is a shared instance.
+     * 
+     * @param type non-null; type of the field in question
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opPutStatic(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return PUT_STATIC_INT;
+            case Type.BT_LONG:    return PUT_STATIC_LONG;
+            case Type.BT_FLOAT:   return PUT_STATIC_FLOAT;
+            case Type.BT_DOUBLE:  return PUT_STATIC_DOUBLE;
+            case Type.BT_OBJECT:  return PUT_STATIC_OBJECT;
+            case Type.BT_BOOLEAN: return PUT_STATIC_BOOLEAN;
+            case Type.BT_BYTE:    return PUT_STATIC_BYTE;
+            case Type.BT_CHAR:    return PUT_STATIC_CHAR;
+            case Type.BT_SHORT:   return PUT_STATIC_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate <code>invoke-static</code> rop for the
+     * given type. The result is typically a newly-allocated instance.
+     * 
+     * @param meth non-null; descriptor of the method
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opInvokeStatic(Prototype meth) {
+        return new Rop(RegOps.INVOKE_STATIC,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate <code>invoke-virtual</code> rop for the
+     * given type. The result is typically a newly-allocated instance.
+     * 
+     * @param meth non-null; descriptor of the method, including the
+     * <code>this</code> parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opInvokeVirtual(Prototype meth) {
+        return new Rop(RegOps.INVOKE_VIRTUAL,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate <code>invoke-super</code> rop for the
+     * given type. The result is typically a newly-allocated instance.
+     * 
+     * @param meth non-null; descriptor of the method, including the
+     * <code>this</code> parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opInvokeSuper(Prototype meth) {
+        return new Rop(RegOps.INVOKE_SUPER,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate <code>invoke-direct</code> rop for the
+     * given type. The result is typically a newly-allocated instance.
+     * 
+     * @param meth non-null; descriptor of the method, including the
+     * <code>this</code> parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opInvokeDirect(Prototype meth) {
+        return new Rop(RegOps.INVOKE_DIRECT,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate <code>invoke-interface</code> rop for the
+     * given type. The result is typically a newly-allocated instance.
+     * 
+     * @param meth non-null; descriptor of the method, including the
+     * <code>this</code> parameter
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opInvokeInterface(Prototype meth) {
+        return new Rop(RegOps.INVOKE_INTERFACE,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+    
+    /**
+     * Returns the appropriate <code>mark-local</code> rop for the given type.
+     * The result is a shared instance.
+     *
+     * @param type non-null; type of value being marked
+     * @return non-null; an appropriate instance
+     */
+    public static Rop opMarkLocal(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MARK_LOCAL_INT;
+            case Type.BT_LONG:   return MARK_LOCAL_LONG;
+            case Type.BT_FLOAT:  return MARK_LOCAL_FLOAT;
+            case Type.BT_DOUBLE: return MARK_LOCAL_DOUBLE;
+            case Type.BT_OBJECT: return MARK_LOCAL_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Rops() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Throws the right exception to complain about a bogus type.
+     * 
+     * @param type non-null; the bad type
+     * @return never
+     */
+    private static Rop throwBadType(TypeBearer type) {
+        throw new IllegalArgumentException("bad type: " + type);
+    }
+
+    /**
+     * Throws the right exception to complain about a bogus list of types.
+     * 
+     * @param types non-null; the bad types
+     * @return never
+     */
+    private static Rop throwBadTypes(TypeList types) {
+        throw new IllegalArgumentException("bad types: " + types);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/SourcePosition.java b/dx/src/com/android/dx/rop/code/SourcePosition.java
new file mode 100644
index 0000000..da66c7d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SourcePosition.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.Hex;
+
+/**
+ * Information about a source position for code, which includes both a
+ * line number and original bytecode address.
+ */
+public final class SourcePosition {
+    /** non-null; convenient "no information known" instance */
+    public static final SourcePosition NO_INFO =
+        new SourcePosition(null, -1, -1);
+
+    /** null-ok; name of the file of origin or <code>null</code> if unknown */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * &gt;= -1; the bytecode address, or <code>-1</code> if that
+     * information is unknown 
+     */
+    private final int address;
+
+    /**
+     * &gt;= -1; the line number, or <code>-1</code> if that
+     * information is unknown 
+     */
+    private final int line;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param sourceFile null-ok; name of the file of origin or
+     * <code>null</code> if unknown
+     * @param address &gt;= -1; original bytecode address or <code>-1</code>
+     * if unknown
+     * @param line &gt;= -1; original line number or <code>-1</code> if
+     * unknown
+     */
+    public SourcePosition(CstUtf8 sourceFile, int address, int line) {
+        if (address < -1) {
+            throw new IllegalArgumentException("address < -1");
+        }
+
+        if (line < -1) {
+            throw new IllegalArgumentException("line < -1");
+        }
+
+        this.sourceFile = sourceFile;
+        this.address = address;
+        this.line = line;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(50);
+
+        if (sourceFile != null) {
+            sb.append(sourceFile.toHuman());
+            sb.append(":");
+        }
+
+        if (line >= 0) {
+            sb.append(line);
+        }
+
+        sb.append('@');
+
+        if (address < 0) {
+            sb.append("????");
+        } else {
+            sb.append(Hex.u2(address));
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof SourcePosition)) {
+            return false;
+        }
+
+        if (this == other) {
+            return true;
+        }
+
+        SourcePosition pos = (SourcePosition) other;
+
+        return (address == pos.address) && sameLineAndFile(pos);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return sourceFile.hashCode() + address + line;
+    }
+
+    /**
+     * Returns whether the lines match between this instance and
+     * the one given.
+     * 
+     * @param other non-null; the instance to compare to
+     * @return <code>true</code> iff the lines match
+     */
+    public boolean sameLine(SourcePosition other) {
+        return (line == other.line);
+    }
+
+    /**
+     * Returns whether the lines and files match between this instance and
+     * the one given.
+     * 
+     * @param other non-null; the instance to compare to
+     * @return <code>true</code> iff the lines and files match
+     */
+    public boolean sameLineAndFile(SourcePosition other) {
+        return (line == other.line) &&
+            ((sourceFile == other.sourceFile) ||
+             ((sourceFile != null) && sourceFile.equals(other.sourceFile)));
+    }
+
+    /**
+     * Gets the source file, if known.
+     * 
+     * @return null-ok; the source file or <code>null</code> if unknown
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+
+    /**
+     * Gets the original bytecode address.
+     * 
+     * @return &gt;= -1; the address or <code>-1</code> if unknown
+     */
+    public int getAddress() {
+        return address;
+    }
+
+    /**
+     * Gets the original line number.
+     * 
+     * @return &gt;= -1; the original line number or <code>-1</code> if
+     * unknown
+     */
+    public int getLine() {
+        return line;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/SwitchInsn.java b/dx/src/com/android/dx/rop/code/SwitchInsn.java
new file mode 100644
index 0000000..fdf1a46
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SwitchInsn.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+/**
+ * Instruction which contains switch cases.
+ */
+public final class SwitchInsn
+        extends Insn {
+    /** non-null; list of switch cases */
+    private final IntList cases;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param result null-ok; spec for the result, if any
+     * @param sources non-null; specs for all the sources
+     * @param cases non-null; list of switch cases
+     */
+    public SwitchInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                      RegisterSpecList sources, IntList cases) {
+        super(opcode, position, result, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_SWITCH) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        this.cases = cases;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return cases.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitSwitchInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new SwitchInsn(getOpcode(), getPosition(),
+                              getResult().withOffset(delta),
+                              getSources().withOffset(delta),
+                              cases);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p> SwitchInsn always compares false. The current use for this method
+     * never encounters <code>SwitchInsn</code>s
+     */
+    @Override
+    public boolean contentEquals(Insn b) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new SwitchInsn(getOpcode(), getPosition(),
+                              result,
+                              sources,
+                              cases);
+    }
+
+    /**
+     * Gets the list of switch cases.
+     * 
+     * @return non-null; the case list
+     */
+    public IntList getCases() {
+        return cases;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
new file mode 100644
index 0000000..49ebc91
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * and which might throw an exception.
+ */
+public final class ThrowingCstInsn
+        extends CstInsn {
+    /** non-null; list of exceptions caught */
+    private final TypeList catches;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param sources non-null; specs for all the sources
+     * @param catches non-null; list of exceptions caught
+     * @param cst non-null; the constant
+     */
+    public ThrowingCstInsn(Rop opcode, SourcePosition position,
+                           RegisterSpecList sources,
+                           TypeList catches, Constant cst) {
+        super(opcode, position, null, sources, cst);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return getConstant().toHuman() + " " +
+                                 ThrowingInsn.toCatchString(catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitThrowingCstInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   getSources(), catches.withAddedType(type),
+                                   getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   getSources().withOffset(delta),
+                                   catches,
+                                   getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   sources,
+                                   catches,
+                                   getConstant());
+    }
+
+
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingInsn.java b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
new file mode 100644
index 0000000..24a5bed
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which possibly throws. The <code>successors</code> list in the
+ * basic block an instance of this class is inside corresponds in-order to
+ * the list of exceptions handled by this instruction, with the
+ * no-exception case appended as the final target.
+ */
+public final class ThrowingInsn
+        extends Insn {
+    /** non-null; list of exceptions caught */
+    private final TypeList catches;
+
+    /**
+     * Gets the string form of a register spec list to be used as a catches
+     * list.
+     * 
+     * @param catches non-null; the catches list
+     * @return non-null; the string form
+     */
+    public static String toCatchString(TypeList catches) {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append("catch");
+
+        int sz = catches.size();
+        for (int i = 0; i < sz; i++) {
+            sb.append(" ");
+            sb.append(catches.getType(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param opcode non-null; the opcode
+     * @param position non-null; source position
+     * @param sources non-null; specs for all the sources
+     * @param catches non-null; list of exceptions caught
+     */
+    public ThrowingInsn(Rop opcode, SourcePosition position,
+                        RegisterSpecList sources,
+                        TypeList catches) {
+        super(opcode, position, null, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return toCatchString(catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitThrowingInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                getSources(), catches.withAddedType(type));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                getSources().withOffset(delta),
+                                catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                sources,
+                                catches);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/TranslationAdvice.java b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
new file mode 100644
index 0000000..8c2cde9
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Interface for "advice" passed from the late stage of translation back
+ * to the early stage. This allows for the final target architecture to
+ * exert its influence early in the translation process without having
+ * the early stage code be explicitly tied to the target.
+ */
+public interface TranslationAdvice {
+    /**
+     * Returns an indication of whether the target can directly represent an
+     * instruction with the given opcode operating on the given arguments,
+     * where the last source argument is used as a constant. (That is, the
+     * last argument must have a type which indicates it is a known constant.)
+     * The instruction associated must have exactly two sources.
+     *
+     * @param opcode non-null; the opcode
+     * @param sourceA non-null; the first source
+     * @param sourceB non-null; the second source
+     * @return <code>true</code> iff the target can represent the operation
+     * using a constant for the last argument
+     */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB);
+
+    /**
+     * Returns true if the translation target requires the sources of the
+     * specified opcode to be in order and contiguous (eg, for an invoke-range)
+     *
+     * @param opcode non-null; opcode
+     * @param sources non-null; source list
+     * @return <code>true</code> iff the target requires the sources to be
+     * in order and contiguous.
+     */
+    public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources);
+
+    /**
+     * Gets the maximum register width that can be represented optimally.
+     * For example, Dex bytecode does not have instruction forms that take
+     * register numbers larger than 15 for all instructions so
+     * DexTranslationAdvice returns 15 here.
+     *
+     * @return register count noted above
+     */
+    public int getMaxOptimalRegisterCount();
+}
diff --git a/dx/src/com/android/dx/rop/code/package.html b/dx/src/com/android/dx/rop/code/package.html
new file mode 100644
index 0000000..86566b4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Classes relating to a register-based opcode system.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/cst/Constant.java b/dx/src/com/android/dx/rop/cst/Constant.java
new file mode 100644
index 0000000..0f44010
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Constant.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Base class for constants of all sorts.
+ */
+public abstract class Constant
+        implements ToHuman, Comparable<Constant> {
+    /**
+     * Returns <code>true</code> if this instance is a category-2 constant,
+     * meaning it takes up two slots in the constant pool, or
+     * <code>false</code> if this instance is category-1.
+     *
+     * @return <code>true</code> iff this instance is category-2
+     */
+    public abstract boolean isCategory2();
+
+    /**
+     * Returns the human name for the particular type of constant
+     * this instance is.
+     *
+     * @return non-null; the name
+     */
+    public abstract String typeName();
+
+    /**
+     * {@inheritDoc}
+     * 
+     * This compares in class-major and value-minor order.
+     */
+    public final int compareTo(Constant other) {
+        Class clazz = getClass();
+        Class otherClazz = other.getClass();
+
+        if (clazz != otherClazz) {
+            return clazz.getName().compareTo(otherClazz.getName());
+        }
+
+        return compareTo0(other);
+    }
+
+    /**
+     * Compare the values of this and another instance, which are guaranteed
+     * to be of the same class. Subclasses must implement this.
+     * 
+     * @param other non-null; the instance to compare to
+     * @return <code>-1</code>, <code>0</code>, or <code>1</code>, as usual
+     * for a comparison
+     */
+    protected abstract int compareTo0(Constant other);
+}
diff --git a/dx/src/com/android/dx/rop/cst/ConstantPool.java b/dx/src/com/android/dx/rop/cst/ConstantPool.java
new file mode 100644
index 0000000..9a64a2a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/ConstantPool.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Interface for constant pools, which are, more or less, just lists of
+ * {@link Constant} objects.
+ */
+public interface ConstantPool {
+    /**
+     * Get the "size" of the constant pool. This corresponds to the
+     * class file field <code>constant_pool_count</code>, and is in fact
+     * always at least one more than the actual size of the constant pool,
+     * as element <code>0</code> is always invalid.
+     *
+     * @return <code>&gt;= 1</code>; the size
+     */
+    public int size();
+
+    /**
+     * Get the <code>n</code>th entry in the constant pool, which must
+     * be valid.
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; the constant pool index
+     * @return non-null; the corresponding entry
+     * @throws IllegalArgumentException thrown if <code>n</code> is
+     * in-range but invalid
+     */
+    public Constant get(int n);
+
+    /**
+     * Get the <code>n</code>th entry in the constant pool, which must
+     * be valid unless <code>n == 0</code>, in which case <code>null</code>
+     * is returned.
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; the constant pool index
+     * @return null-ok; the corresponding entry, if <code>n != 0</code>
+     * @throws IllegalArgumentException thrown if <code>n</code> is
+     * in-range and non-zero but invalid
+     */
+    public Constant get0Ok(int n);
+
+    /**
+     * Get the <code>n</code>th entry in the constant pool, or
+     * <code>null</code> if the index is in-range but invalid. In
+     * particular, <code>null</code> is returned for index <code>0</code>
+     * as well as the index after any entry which is defined to take up
+     * two slots (that is, <code>Long</code> and <code>Double</code>
+     * entries).
+     *
+     * @param n <code>n &gt;= 0, n &lt; size()</code>; the constant pool index
+     * @return null-ok; the corresponding entry, or <code>null</code> if
+     * the index is in-range but invalid
+     */
+    public Constant getOrNull(int n);
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstAnnotation.java b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
new file mode 100644
index 0000000..d6dc1f2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.annotation.Annotation;
+
+/**
+ * Constant type that represents an annotation.
+ */
+public final class CstAnnotation extends Constant {
+    /** non-null; the actual annotation */
+    private final Annotation annotation;
+    
+    /**
+     * Constructs an instance.
+     *
+     * @param annotation non-null; the annotation to hold
+     */
+    public CstAnnotation(Annotation annotation) {
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        annotation.throwIfMutable();
+
+        this.annotation = annotation;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof CstAnnotation)) {
+            return false;
+        }
+
+        return annotation.equals(((CstAnnotation) other).annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotation.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return annotation.compareTo(((CstAnnotation) other).annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return annotation.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "annotation";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return annotation.toString();
+    }
+
+    /**
+     * Get the underlying annotation.
+     * 
+     * @return non-null; the annotation
+     */
+    public Annotation getAnnotation() {
+        return annotation;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstArray.java b/dx/src/com/android/dx/rop/cst/CstArray.java
new file mode 100644
index 0000000..69c0aef
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstArray.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Constant type to represent a fixed array of other constants. The contents
+ * may be of any type <i>other</i> than {@link CstUtf8}.
+ */
+public final class CstArray extends Constant {
+    /** non-null; the actual list of contents */
+    private final List list;
+    
+    /**
+     * Constructs an instance.
+     *
+     * @param list non-null; the actual list of contents
+     */
+    public CstArray(List list) {
+        if (list == null) {
+            throw new NullPointerException("list == null");
+        }
+
+        list.throwIfMutable();
+
+        this.list = list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof CstArray)) {
+            return false;
+        }
+
+        return list.equals(((CstArray) other).list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return list.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return list.compareTo(((CstArray) other).list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return list.toString("array{", ", ", "}");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "array";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return list.toHuman("{", ", ", "}");
+    }
+
+    /**
+     * Get the underlying list.
+     * 
+     * @return non-null; the list
+     */
+    public List getList() {
+        return list;
+    }
+
+    /**
+     * List of {@link Constant} instances.
+     */
+    public static final class List
+            extends FixedSizeList implements Comparable<List> {
+        /**
+         * Constructs an instance. All indices initially contain
+         * <code>null</code>.
+         * 
+         * @param size the size of the list
+         */
+        public List(int size) {
+            super(size);
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(List other) {
+            int thisSize = size();
+            int otherSize = other.size();
+            int compareSize = (thisSize < otherSize) ? thisSize : otherSize;
+
+            for (int i = 0; i < compareSize; i++) {
+                Constant thisItem = (Constant) get0(i);
+                Constant otherItem = (Constant) other.get0(i);
+                int compare = thisItem.compareTo(otherItem);
+                if (compare != 0) {
+                    return compare;
+                }
+            }
+
+            if (thisSize < otherSize) {
+                return -1;
+            } else if (thisSize > otherSize) {
+                return 1;
+            }
+
+            return 0;
+        }
+
+        /**
+         * Gets the element at the given index. It is an error to call
+         * this with the index for an element which was never set; if you
+         * do that, this will throw <code>NullPointerException</code>.
+         * 
+         * @param n &gt;= 0, &lt; size(); which index
+         * @return non-null; element at that index
+         */
+        public Constant get(int n) {
+            return (Constant) get0(n);
+        }
+
+        /**
+         * Sets the element at the given index.
+         * 
+         * @param n &gt;= 0, &lt; size(); which index
+         * @param a null-ok; the element to set at <code>n</code>
+         */
+        public void set(int n, Constant a) {
+            if (a instanceof CstUtf8) {
+                throw new IllegalArgumentException("bad value: " + a);
+            }
+
+            set0(n, a);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
new file mode 100644
index 0000000..c885601
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants of "methodish" type.
+ *
+ * <p><b>Note:</b> As a {@link TypeBearer}, this class bears the return type
+ * of the method.</p>
+ */
+public abstract class CstBaseMethodRef
+        extends CstMemberRef {
+    /** non-null; the raw prototype for this method */
+    private final Prototype prototype;
+
+    /**
+     * null-ok; the prototype for this method taken to be an instance
+     * method, or <code>null</code> if not yet calculated
+     */
+    private Prototype instancePrototype;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass non-null; the type of the defining class
+     * @param nat non-null; the name-and-type
+     */
+    /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+
+        String descriptor = getNat().getDescriptor().getString();
+        this.prototype = Prototype.intern(descriptor);
+        this.instancePrototype = null;
+    }
+
+    /**
+     * Gets the raw prototype of this method. This doesn't include a
+     * <code>this</code> argument.
+     *
+     * @return non-null; the method prototype
+     */
+    public final Prototype getPrototype() {
+        return prototype;
+    }
+
+    /**
+     * Gets the prototype of this method as either a
+     * <code>static</code> or instance method. In the case of a
+     * <code>static</code> method, this is the same as the raw
+     * prototype. In the case of an instance method, this has an
+     * appropriately-typed <code>this</code> argument as the first
+     * one.
+     *
+     * @param isStatic whether the method should be considered static
+     * @return non-null; the method prototype
+     */
+    public final Prototype getPrototype(boolean isStatic) {
+        if (isStatic) {
+            return prototype;
+        } else {
+            if (instancePrototype == null) {
+                Type thisType = getDefiningClass().getClassType();
+                instancePrototype = prototype.withFirstParameter(thisType);
+            }
+            return instancePrototype;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final int compareTo0(Constant other) {
+        int cmp = super.compareTo0(other);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstBaseMethodRef otherMethod = (CstBaseMethodRef) other;
+        return prototype.compareTo(otherMethod.prototype);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * In this case, this method returns the <i>return type</i> of this method.
+     *
+     * @return non-null; the method's return type
+     */
+    public final Type getType() {
+        return prototype.getReturnType();
+    }
+
+    /**
+     * Gets the number of words of parameters required by this
+     * method's descriptor. Since instances of this class have no way
+     * to know if they will be used in a <code>static</code> or
+     * instance context, one has to indicate this explicitly as an
+     * argument. This method is just a convenient shorthand for
+     * <code>getPrototype().getParameterTypes().getWordCount()</code>,
+     * plus <code>1</code> if the method is to be treated as an
+     * instance method.
+     * 
+     * @param isStatic whether the method should be considered static
+     * @return &gt;= 0; the argument word count
+     */
+    public final int getParameterWordCount(boolean isStatic) {
+        return getPrototype(isStatic).getParameterTypes().getWordCount();
+    }
+
+    /**
+     * Gets whether this is a reference to an instance initialization
+     * method. This is just a convenient shorthand for
+     * <code>getNat().isInstanceInit()</code>.
+     *
+     * @return <code>true</code> iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isInstanceInit() {
+        return getNat().isInstanceInit();
+    }
+
+    /**
+     * Gets whether this is a reference to a class initialization
+     * method. This is just a convenient shorthand for
+     * <code>getNat().isClassInit()</code>.
+     *
+     * @return <code>true</code> iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isClassInit() {
+        return getNat().isClassInit();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBoolean.java b/dx/src/com/android/dx/rop/cst/CstBoolean.java
new file mode 100644
index 0000000..ab25d5b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBoolean.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type <code>boolean</code>.
+ */
+public final class CstBoolean
+        extends CstLiteral32 {
+    /** non-null; instance representing <code>false</code> */
+    public static final CstBoolean VALUE_FALSE = new CstBoolean(false);
+
+    /** non-null; instance representing <code>true</code> */
+    public static final CstBoolean VALUE_TRUE = new CstBoolean(true);
+
+    /**
+     * Makes an instance for the given value. This will return an
+     * already-allocated instance.
+     * 
+     * @param value the <code>boolean</code> value
+     * @return non-null; the appropriate instance
+     */
+    public static CstBoolean make(boolean value) {
+        return value ? VALUE_TRUE : VALUE_FALSE;
+    }
+
+    /**
+     * Makes an instance for the given <code>int</code> value. This
+     * will return an already-allocated instance.
+     * 
+     * @param value must be either <code>0</code> or <code>1</code>
+     * @return non-null; the appropriate instance
+     */
+    public static CstBoolean make(int value) {
+        if (value == 0) {
+            return VALUE_FALSE;
+        } else if (value == 1) {
+            return VALUE_TRUE;
+        } else {
+            throw new IllegalArgumentException("bogus value: " + value);
+        }
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>boolean</code> value
+     */
+    private CstBoolean(boolean value) {
+        super(value ? 1 : 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return getValue() ? "boolean{true}" : "boolean{false}";
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.BOOLEAN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "boolean";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return getValue() ? "true" : "false";
+    }
+
+    /**
+     * Gets the <code>boolean</code> value.
+     * 
+     * @return the value
+     */
+    public boolean getValue() {
+        return (getIntBits() == 0) ? false : true;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstByte.java b/dx/src/com/android/dx/rop/cst/CstByte.java
new file mode 100644
index 0000000..ffc3206
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstByte.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>byte</code>.
+ */
+public final class CstByte
+        extends CstLiteral32 {
+    /** non-null; the value <code>0</code> as an instance of this class */
+    public static final CstByte VALUE_0 = make((byte) 0);
+    
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param value the <code>byte</code> value
+     */
+    public static CstByte make(byte value) {
+        return new CstByte(value);
+    }
+
+    /**
+     * Makes an instance for the given <code>int</code> value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     * 
+     * @param value the value, which must be in range for a <code>byte</code>
+     * @return non-null; the appropriate instance
+     */
+    public static CstByte make(int value) {
+        byte cast = (byte) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus byte value: " + 
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>byte</code> value
+     */
+    private CstByte(byte value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "byte{0x" + Hex.u1(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.BYTE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "byte";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the <code>byte</code> value.
+     * 
+     * @return the value
+     */
+    public byte getValue() {
+        return (byte) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstChar.java b/dx/src/com/android/dx/rop/cst/CstChar.java
new file mode 100644
index 0000000..a31bd7f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstChar.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>char</code>.
+ */
+public final class CstChar
+        extends CstLiteral32 {
+    /** non-null; the value <code>0</code> as an instance of this class */
+    public static final CstChar VALUE_0 = make((char) 0);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param value the <code>char</code> value
+     */
+    public static CstChar make(char value) {
+        return new CstChar(value);
+    }
+
+    /**
+     * Makes an instance for the given <code>int</code> value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     * 
+     * @param value the value, which must be in range for a <code>char</code>
+     * @return non-null; the appropriate instance
+     */
+    public static CstChar make(int value) {
+        char cast = (char) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus char value: " + 
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>char</code> value
+     */
+    private CstChar(char value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "char{0x" + Hex.u2(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.CHAR;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "char";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the <code>char</code> value.
+     * 
+     * @return the value
+     */
+    public char getValue() {
+        return (char) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstDouble.java b/dx/src/com/android/dx/rop/cst/CstDouble.java
new file mode 100644
index 0000000..4516667
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstDouble.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>CONSTANT_Double_info</code>.
+ */
+public final class CstDouble
+        extends CstLiteral64 {
+    /** non-null; instance representing <code>0</code> */
+    public static final CstDouble VALUE_0 =
+        new CstDouble(Double.doubleToLongBits(0.0));
+
+    /** non-null; instance representing <code>1</code> */
+    public static final CstDouble VALUE_1 =
+        new CstDouble(Double.doubleToLongBits(1.0));
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param bits the <code>double</code> value as <code>long</code> bits
+     */
+    public static CstDouble make(long bits) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstDouble(bits);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param bits the <code>double</code> value as <code>long</code> bits
+     */
+    private CstDouble(long bits) {
+        super(bits);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        long bits = getLongBits();
+        return "double{0x" + Hex.u8(bits) + " / " +
+            Double.longBitsToDouble(bits) + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.DOUBLE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "double";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Double.toString(Double.longBitsToDouble(getLongBits()));
+    }
+
+    /**
+     * Gets the <code>double</code> value.
+     * 
+     * @return the value
+     */
+    public double getValue() {
+        return Double.longBitsToDouble(getLongBits());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstEnumRef.java b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
new file mode 100644
index 0000000..f5aec05
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a reference to a particular constant
+ * value of an enumerated type.
+ */
+public final class CstEnumRef extends CstMemberRef {
+    /** null-ok; the corresponding field ref, lazily initialized */
+    private CstFieldRef fieldRef;
+    
+    /**
+     * Constructs an instance.
+     *
+     * @param nat non-null; the name-and-type; the defining class is derived
+     * from this
+     */
+    public CstEnumRef(CstNat nat) {
+        super(new CstType(nat.getFieldType()), nat);
+
+        fieldRef = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "enum";
+    }
+
+    /**
+     * {@inheritDoc} 
+     * 
+     * <b>Note:</b> This returns the enumerated type.
+     */
+    public Type getType() {
+        return getDefiningClass().getClassType();
+    }
+
+    /**
+     * Get a {@link CstFieldRef} that corresponds with this instance.
+     * 
+     * @return non-null; the corresponding field reference
+     */
+    public CstFieldRef getFieldRef() {
+        if (fieldRef == null) {
+            fieldRef = new CstFieldRef(getDefiningClass(), getNat());
+        }
+
+        return fieldRef;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFieldRef.java b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
new file mode 100644
index 0000000..306eca9
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type <code>CONSTANT_Fieldref_info</code>.
+ */
+public final class CstFieldRef extends CstMemberRef {
+    /**
+     * Returns an instance of this class that represents the static
+     * field which should hold the class corresponding to a given
+     * primitive type. For example, if given {@link Type#INT}, this
+     * method returns an instance corresponding to the field
+     * <code>java.lang.Integer.TYPE</code>.
+     * 
+     * @param primitiveType non-null; the primitive type
+     * @return non-null; the corresponding static field
+     */
+    public static CstFieldRef forPrimitiveType(Type primitiveType) {
+        return new CstFieldRef(CstType.forBoxedPrimitiveType(primitiveType),
+                CstNat.PRIMITIVE_TYPE_NAT);
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the type of the defining class
+     * @param nat non-null; the name-and-type
+     */
+    public CstFieldRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "field";
+    }
+
+    /**
+     * Returns the type of this field.
+     * 
+     * @return non-null; the field's type
+     */
+    public Type getType() {
+        return getNat().getFieldType();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        int cmp = super.compareTo0(other);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstFieldRef otherField = (CstFieldRef) other;
+        CstUtf8 thisDescriptor = getNat().getDescriptor();
+        CstUtf8 otherDescriptor = otherField.getNat().getDescriptor();
+        return thisDescriptor.compareTo(otherDescriptor);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFloat.java b/dx/src/com/android/dx/rop/cst/CstFloat.java
new file mode 100644
index 0000000..08b7f76
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFloat.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>CONSTANT_Float_info</code>.
+ */
+public final class CstFloat
+        extends CstLiteral32 {
+    /** non-null; instance representing <code>0</code> */
+    public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f));
+
+    /** non-null; instance representing <code>1</code> */
+    public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f));
+
+    /** non-null; instance representing <code>2</code> */
+    public static final CstFloat VALUE_2 = make(Float.floatToIntBits(2.0f));
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param bits the <code>float</code> value as <code>int</code> bits
+     */
+    public static CstFloat make(int bits) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstFloat(bits);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param bits the <code>float</code> value as <code>int</code> bits
+     */
+    private CstFloat(int bits) {
+        super(bits);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int bits = getIntBits();
+        return "float{0x" + Hex.u4(bits) + " / " +
+            Float.intBitsToFloat(bits) + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.FLOAT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "float";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Float.toString(Float.intBitsToFloat(getIntBits()));
+    }
+
+    /**
+     * Gets the <code>float</code> value.
+     * 
+     * @return the value
+     */
+    public float getValue() {
+        return Float.intBitsToFloat(getIntBits());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInteger.java b/dx/src/com/android/dx/rop/cst/CstInteger.java
new file mode 100644
index 0000000..d3fafcc
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInteger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>CONSTANT_Integer_info</code>.
+ */
+public final class CstInteger
+        extends CstLiteral32 {
+    /** non-null; array of cached instances */
+    private static final CstInteger[] cache = new CstInteger[511];
+
+    /** non-null; instance representing <code>-1</code> */
+    public static final CstInteger VALUE_M1 = make(-1);
+
+    /** non-null; instance representing <code>0</code> */
+    public static final CstInteger VALUE_0 = make(0);
+
+    /** non-null; instance representing <code>1</code> */
+    public static final CstInteger VALUE_1 = make(1);
+
+    /** non-null; instance representing <code>2</code> */
+    public static final CstInteger VALUE_2 = make(2);
+
+    /** non-null; instance representing <code>3</code> */
+    public static final CstInteger VALUE_3 = make(3);
+
+    /** non-null; instance representing <code>4</code> */
+    public static final CstInteger VALUE_4 = make(4);
+
+    /** non-null; instance representing <code>5</code> */
+    public static final CstInteger VALUE_5 = make(5);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param value the <code>int</code> value
+     * @return non-null; the appropriate instance
+     */
+    public static CstInteger make(int value) {
+        /*
+         * Note: No need to synchronize, since we don't make any sort
+         * of guarantee about ==, and it's okay to overwrite existing
+         * entries too.
+         */
+        int idx = (value & 0x7fffffff) % cache.length;
+        CstInteger obj = cache[idx];
+
+        if ((obj != null) && (obj.getValue() == value)) {
+            return obj;
+        }
+
+        obj = new CstInteger(value);
+        cache[idx] = obj;
+        return obj;
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>int</code> value
+     */
+    private CstInteger(int value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "int{0x" + Hex.u4(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.INT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "int";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the <code>int</code> value.
+     * 
+     * @return the value
+     */
+    public int getValue() {
+        return getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
new file mode 100644
index 0000000..f169ec9
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type <code>CONSTANT_InterfaceMethodref_info</code>.
+ */
+public final class CstInterfaceMethodRef
+        extends CstBaseMethodRef {
+    /**
+     * null-ok; normal {@link CstMethodRef} that corresponds to this
+     * instance, if calculated 
+     */
+    private CstMethodRef methodRef;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the type of the defining class
+     * @param nat non-null; the name-and-type
+     */
+    public CstInterfaceMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+        methodRef = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "ifaceMethod";
+    }
+
+    /**
+     * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to
+     * this instance.
+     * 
+     * @return non-null; an appropriate instance
+     */
+    public CstMethodRef toMethodRef() {
+        if (methodRef == null) {
+            methodRef = new CstMethodRef(getDefiningClass(), getNat());
+        }
+
+        return methodRef;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstKnownNull.java b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
new file mode 100644
index 0000000..853e57e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a known-<code>null</code> value.
+ */
+public final class CstKnownNull extends CstLiteralBits {
+    /** non-null; unique instance of this class */
+    public static final CstKnownNull THE_ONE = new CstKnownNull();
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable. Use
+     * {@link #THE_ONE}.
+     */
+    private CstKnownNull() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        return (other instanceof CstKnownNull);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return 0x4466757a;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "known-null";
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.KNOWN_NULL;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "known-null";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return "null";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean fitsInInt() {
+        // See comment in getIntBits().
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * As "literal bits," a known-null is always represented as the
+     * number zero.
+     */
+    @Override
+    public int getIntBits() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * As "literal bits," a known-null is always represented as the
+     * number zero.
+     */
+    @Override
+    public long getLongBits() {
+        return 0;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral32.java b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
new file mode 100644
index 0000000..31e96dd
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 32-bit values of some sort.
+ */
+public abstract class CstLiteral32
+        extends CstLiteralBits {
+    /** the value as <code>int</code> bits */
+    private final int bits;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bits the value as <code>int</code> bits
+     */
+    /*package*/ CstLiteral32(int bits) {
+        this.bits = bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        return (other != null) &&
+            (getClass() == other.getClass()) &&
+            bits == ((CstLiteral32) other).bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        int otherBits = ((CstLiteral32) other).bits;
+
+        if (bits < otherBits) {
+            return -1;
+        } else if (bits > otherBits) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean fitsInInt() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getIntBits() {
+        return bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final long getLongBits() {
+        return (long) bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral64.java b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
new file mode 100644
index 0000000..dd7d24d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 64-bit values of some sort.
+ */
+public abstract class CstLiteral64
+        extends CstLiteralBits {
+    /** the value as <code>long</code> bits */
+    private final long bits;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param bits the value as <code>long</code> bits
+     */
+    /*package*/ CstLiteral64(long bits) {
+        this.bits = bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        return (other != null) &&
+            (getClass() == other.getClass()) &&
+            bits == ((CstLiteral64) other).bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return (int) bits ^ (int) (bits >> 32);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        long otherBits = ((CstLiteral64) other).bits;
+
+        if (bits < otherBits) {
+            return -1;
+        } else if (bits > otherBits) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean fitsInInt() {
+        return (int) bits == bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getIntBits() {
+        return (int) bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final long getLongBits() {
+        return bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteralBits.java b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
new file mode 100644
index 0000000..98a3f0e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal bitwise values of some sort.
+ */
+public abstract class CstLiteralBits
+        extends TypedConstant {
+    /**
+     * Returns whether or not this instance's value may be accurately
+     * represented as an <code>int</code>. The rule is that if there
+     * is an <code>int</code> which may be sign-extended to yield this
+     * instance's value, then this method returns <code>true</code>.
+     * Otherwise, it returns <code>false</code>.
+     *
+     * @return <code>true</code> iff this instance fits in an <code>int</code>
+     */
+    public abstract boolean fitsInInt();
+
+    /**
+     * Gets the value as <code>int</code> bits. If this instance contains
+     * more bits than fit in an <code>int</code>, then this returns only
+     * the low-order bits.
+     *
+     * @return the bits
+     */
+    public abstract int getIntBits();
+
+    /**
+     * Gets the value as <code>long</code> bits. If this instance contains
+     * fewer bits than fit in a <code>long</code>, then the result of this
+     * method is the sign extension of the value.
+     * 
+     * @return the bits
+     */
+    public abstract long getLongBits();
+
+    /**
+     * Returns true if this value can fit in 16 bits with sign-extension.
+     *
+     * @return true if the sign-extended lower 16 bits are the same as
+     * the value.
+     */
+    public boolean fitsIn16Bits() {
+        if (! fitsInInt()) {
+            return false;
+        }
+        
+        int bits = getIntBits();
+        return (short) bits == bits;
+    }
+
+    /**
+     * Returns true if this value can fit in 8 bits with sign-extension.
+     *
+     * @return true if the sign-extended lower 8 bits are the same as
+     * the value.
+     */
+    public boolean fitsIn8Bits() {
+        if (! fitsInInt()) {
+            return false;
+        }
+
+        int bits = getIntBits();
+        return (byte) bits == bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLong.java b/dx/src/com/android/dx/rop/cst/CstLong.java
new file mode 100644
index 0000000..377eb93
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLong.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>CONSTANT_Long_info</code>.
+ */
+public final class CstLong
+        extends CstLiteral64 {
+    /** non-null; instance representing <code>0</code> */
+    public static final CstLong VALUE_0 = make(0);
+
+    /** non-null; instance representing <code>1</code> */
+    public static final CstLong VALUE_1 = make(1);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param value the <code>long</code> value
+     */
+    public static CstLong make(long value) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstLong(value);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>long</code> value
+     */
+    private CstLong(long value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        long value = getLongBits();
+        return "long{0x" + Hex.u8(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.LONG;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "long";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Long.toString(getLongBits());
+    }
+
+    /**
+     * Gets the <code>long</code> value.
+     * 
+     * @return the value
+     */
+    public long getValue() {
+        return getLongBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMemberRef.java b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
new file mode 100644
index 0000000..dbaad47
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type <code>CONSTANT_*ref_info</code>.
+ */
+public abstract class CstMemberRef extends TypedConstant {
+    /** non-null; the type of the defining class */
+    private final CstType definingClass;
+
+    /** non-null; the name-and-type */
+    private final CstNat nat;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the type of the defining class
+     * @param nat non-null; the name-and-type
+     */
+    /*package*/ CstMemberRef(CstType definingClass, CstNat nat) {
+        if (definingClass == null) {
+            throw new NullPointerException("definingClass == null");
+        }
+
+        if (nat == null) {
+            throw new NullPointerException("nat == null");
+        }
+
+        this.definingClass = definingClass;
+        this.nat = nat;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        if ((other == null) || (getClass() != other.getClass())) {
+            return false;
+        }
+
+        CstMemberRef otherRef = (CstMemberRef) other;
+        return definingClass.equals(otherRef.definingClass) && 
+            nat.equals(otherRef.nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return (definingClass.hashCode() * 31) ^ nat.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>Note:</b> This implementation just compares the defining
+     * class and name, and it is up to subclasses to compare the rest
+     * after calling <code>super.compareTo0()</code>.</p>
+     */
+    @Override
+    protected int compareTo0(Constant other) {
+        CstMemberRef otherMember = (CstMemberRef) other;
+        int cmp = definingClass.compareTo(otherMember.definingClass);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstUtf8 thisName = nat.getName();
+        CstUtf8 otherName = otherMember.nat.getName();
+
+        return thisName.compareTo(otherName);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toString() {
+        return typeName() + '{' + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public final String toHuman() {
+        return definingClass.toHuman() + '.' + nat.toHuman();
+    }
+
+    /**
+     * Gets the type of the defining class.
+     * 
+     * @return non-null; the type of defining class
+     */
+    public final CstType getDefiningClass() {
+        return definingClass;
+    }
+
+    /**
+     * Gets the defining name-and-type.
+     * 
+     * @return non-null; the name-and-type
+     */
+    public final CstNat getNat() {
+        return nat;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMethodRef.java b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
new file mode 100644
index 0000000..766c9bf
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type <code>CONSTANT_Methodref_info</code>.
+ */
+public final class CstMethodRef
+        extends CstBaseMethodRef {
+    /**
+     * Constructs an instance.
+     * 
+     * @param definingClass non-null; the type of the defining class
+     * @param nat non-null; the name-and-type
+     */
+    public CstMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "method";
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstNat.java b/dx/src/com/android/dx/rop/cst/CstNat.java
new file mode 100644
index 0000000..106b599
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstNat.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type <code>CONSTANT_NameAndType_info</code>.
+ */
+public final class CstNat extends Constant {
+    /**
+     * non-null; the instance for name <code>TYPE</code> and descriptor
+     * <code>java.lang.Class</code>, which is useful when dealing with
+     * wrapped primitives 
+     */
+    public static final CstNat PRIMITIVE_TYPE_NAT =
+        new CstNat(new CstUtf8("TYPE"),
+                   new CstUtf8("Ljava/lang/Class;"));
+
+    /** non-null; the name */
+    private final CstUtf8 name;
+
+    /** non-null; the descriptor (type) */
+    private final CstUtf8 descriptor;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param name non-null; the name
+     * @param descriptor non-null; the descriptor
+     */
+    public CstNat(CstUtf8 name, CstUtf8 descriptor) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        this.name = name;
+        this.descriptor = descriptor;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstNat)) {
+            return false;
+        }
+
+        CstNat otherNat = (CstNat) other;
+        return name.equals(otherNat.name) && 
+            descriptor.equals(otherNat.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return (name.hashCode() * 31) ^ descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        CstNat otherNat = (CstNat) other;
+        int cmp = name.compareTo(otherNat.name);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        return descriptor.compareTo(otherNat.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "nat{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "nat";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /**
+     * Gets the name.
+     * 
+     * @return non-null; the name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+
+    /**
+     * Gets the descriptor.
+     * 
+     * @return non-null; the descriptor
+     */
+    public CstUtf8 getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Returns an unadorned but human-readable version of the name-and-type
+     * value.
+     * 
+     * @return non-null; the human form
+     */
+    public String toHuman() {
+        return name.toHuman() + ':' + descriptor.toHuman();
+    }
+
+    /**
+     * Gets the field type corresponding to this instance's descriptor.
+     * This method is only valid to call if the descriptor in fact describes
+     * a field (and not a method).
+     * 
+     * @return non-null; the field type
+     */
+    public Type getFieldType() {
+        return Type.intern(descriptor.getString());
+    }
+
+    /**
+     * Gets whether this instance has the name of a standard instance
+     * initialization method. This is just a convenient shorthand for
+     * <code>getName().getString().equals("&lt;init&gt;")</code>.
+     * 
+     * @return <code>true</code> iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isInstanceInit() {
+        return name.getString().equals("<init>");
+    }
+
+    /**
+     * Gets whether this instance has the name of a standard class
+     * initialization method. This is just a convenient shorthand for
+     * <code>getName().getString().equals("&lt;clinit&gt;")</code>.
+     * 
+     * @return <code>true</code> iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isClassInit() {
+        return name.getString().equals("<clinit>");
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstShort.java b/dx/src/com/android/dx/rop/cst/CstShort.java
new file mode 100644
index 0000000..3804254
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstShort.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>short</code>.
+ */
+public final class CstShort
+        extends CstLiteral32 {
+    /** non-null; the value <code>0</code> as an instance of this class */
+    public static final CstShort VALUE_0 = make((short) 0);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     * 
+     * @param value the <code>short</code> value
+     * @return non-null; the appropriate instance
+     */
+    public static CstShort make(short value) {
+        return new CstShort(value);
+    }
+
+    /**
+     * Makes an instance for the given <code>int</code> value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     * 
+     * @param value the value, which must be in range for a <code>short</code>
+     * @return non-null; the appropriate instance
+     */
+    public static CstShort make(int value) {
+        short cast = (short) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus short value: " + 
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     * 
+     * @param value the <code>short</code> value
+     */
+    private CstShort(short value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "short{0x" + Hex.u2(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.SHORT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "short";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the <code>short</code> value.
+     * 
+     * @return the value
+     */
+    public short getValue() {
+        return (short) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstString.java b/dx/src/com/android/dx/rop/cst/CstString.java
new file mode 100644
index 0000000..89a4c8b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstString.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type <code>CONSTANT_String_info</code>.
+ */
+public final class CstString
+        extends TypedConstant {
+    /** non-null; the string value */
+    private final CstUtf8 string;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param string non-null; the string value
+     */
+    public CstString(CstUtf8 string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        this.string = string;
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param string non-null; the string value
+     */
+    public CstString(String string) {
+        this(new CstUtf8(string));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstString)) {
+            return false;
+        }
+
+        return string.equals(((CstString) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return string.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return string.compareTo(((CstString) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "string{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.STRING;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "string";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return string.toQuoted();
+    }
+
+    /**
+     * Gets the string value.
+     * 
+     * @return non-null; the string value
+     */
+    public CstUtf8 getString() {
+        return string;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstType.java b/dx/src/com/android/dx/rop/cst/CstType.java
new file mode 100644
index 0000000..02df28d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstType.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Constants that represent an arbitrary type (reference or primitive).
+ */
+public final class CstType extends TypedConstant {
+    /** non-null; map of interned types */
+    private static final HashMap<Type, CstType> interns =
+        new HashMap<Type, CstType>(100);
+
+    /** non-null; instance corresponding to the class <code>Object</code> */
+    public static final CstType OBJECT = intern(Type.OBJECT);
+
+    /** non-null; instance corresponding to the class <code>Boolean</code> */
+    public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Byte</code> */
+    public static final CstType BYTE = intern(Type.BYTE_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Character</code> */
+    public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Double</code> */
+    public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Float</code> */
+    public static final CstType FLOAT = intern(Type.FLOAT_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Long</code> */
+    public static final CstType LONG = intern(Type.LONG_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Integer</code> */
+    public static final CstType INTEGER = intern(Type.INTEGER_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Short</code> */
+    public static final CstType SHORT = intern(Type.SHORT_CLASS);
+
+    /** non-null; instance corresponding to the class <code>Void</code> */
+    public static final CstType VOID = intern(Type.VOID_CLASS);
+
+    /** non-null; instance corresponding to the type <code>boolean[]</code> */
+    public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>byte[]</code> */
+    public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>char[]</code> */
+    public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>double[]</code> */
+    public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>float[]</code> */
+    public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>long[]</code> */
+    public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>int[]</code> */
+    public static final CstType INT_ARRAY = intern(Type.INT_ARRAY);
+
+    /** non-null; instance corresponding to the type <code>short[]</code> */
+    public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY);
+
+    /** non-null; the underlying type */
+    private final Type type;
+
+    /**
+     * null-ok; the type descriptor corresponding to this instance, if
+     * calculated
+     */
+    private CstUtf8 descriptor;
+
+    /**
+     * Returns an instance of this class that represents the wrapper
+     * class corresponding to a given primitive type. For example, if
+     * given {@link Type#INT}, this method returns the class reference
+     * <code>java.lang.Integer</code>.
+     * 
+     * @param primitiveType non-null; the primitive type
+     * @return non-null; the corresponding wrapper class
+     */
+    public static CstType forBoxedPrimitiveType(Type primitiveType) {
+        switch (primitiveType.getBasicType()) {
+            case Type.BT_BOOLEAN: return BOOLEAN;
+            case Type.BT_BYTE:    return BYTE;
+            case Type.BT_CHAR:    return CHARACTER;
+            case Type.BT_DOUBLE:  return DOUBLE;
+            case Type.BT_FLOAT:   return FLOAT;
+            case Type.BT_INT:     return INTEGER;
+            case Type.BT_LONG:    return LONG;
+            case Type.BT_SHORT:   return SHORT;
+            case Type.BT_VOID:    return VOID;
+        }
+
+        throw new IllegalArgumentException("not primitive: " + primitiveType);
+    }
+
+    /**
+     * Returns an interned instance of this class for the given type.
+     * 
+     * @param type non-null; the underlying type
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static CstType intern(Type type) {
+        CstType cst = interns.get(type);
+
+        if (cst == null) {
+            cst = new CstType(type);
+            interns.put(type, cst);
+        }
+
+        return cst;
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param type non-null; the underlying type
+     */
+    public CstType(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (type == type.KNOWN_NULL) {
+            throw new UnsupportedOperationException(
+                    "KNOWN_NULL is not representable");
+        }
+
+        this.type = type;
+        this.descriptor = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstType)) {
+            return false;
+        }
+
+        return type == ((CstType) other).type;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return type.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        String thisDescriptor = type.getDescriptor();
+        String otherDescriptor = ((CstType) other).type.getDescriptor();
+        return thisDescriptor.compareTo(otherDescriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "type{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.CLASS;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "type";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return type.toHuman();
+    }
+
+    /**
+     * Gets the underlying type (as opposed to the type corresponding
+     * to this instance as a constant, which is always
+     * <code>Class</code>).
+     * 
+     * @return non-null; the type corresponding to the name
+     */
+    public Type getClassType() {
+        return type;
+    }
+
+    /**
+     * Gets the type descriptor for this instance.
+     * 
+     * @return non-null; the descriptor
+     */
+    public CstUtf8 getDescriptor() {
+        if (descriptor == null) {
+            descriptor = new CstUtf8(type.getDescriptor());
+        }
+        
+        return descriptor;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstUtf8.java b/dx/src/com/android/dx/rop/cst/CstUtf8.java
new file mode 100644
index 0000000..f0ca5f5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstUtf8.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type <code>CONSTANT_Utf8_info</code>.
+ */
+public final class CstUtf8 extends Constant {
+    /** 
+     * non-null; instance representing <code>""</code>, that is, the
+     * empty string 
+     */
+    public static final CstUtf8 EMPTY_STRING = new CstUtf8("");
+    
+    /** non-null; the UTF-8 value as a string */
+    private final String string;
+
+    /** non-null; the UTF-8 value as bytes */
+    private final ByteArray bytes;
+
+    /**
+     * Converts a string into its Java-style UTF-8 form. Java-style UTF-8
+     * differs from normal UTF-8 in the handling of character '\0' and
+     * surrogate pairs.
+     * 
+     * @param string non-null; the string to convert
+     * @return non-null; the UTF-8 bytes for it
+     */
+    public static byte[] stringToUtf8Bytes(String string) {
+        int len = string.length();
+        byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
+        int outAt = 0;
+
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            if ((c != 0) && (c < 0x80)) {
+                bytes[outAt] = (byte) c;
+                outAt++;
+            } else if (c < 0x800) {
+                bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
+                bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
+                outAt += 2;
+            } else {
+                bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
+                bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
+                bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
+                outAt += 3;
+            }
+        }
+
+        byte[] result = new byte[outAt];
+        System.arraycopy(bytes, 0, result, 0, outAt);
+        return result;
+    }
+
+    /**
+     * Converts an array of UTF-8 bytes into a string.
+     * 
+     * @param bytes non-null; the bytes to convert
+     * @return non-null; the converted string
+     */
+    public static String utf8BytesToString(ByteArray bytes) {
+        int length = bytes.size();
+        char[] chars = new char[length]; // This is sized to avoid a realloc.
+        int outAt = 0;
+
+        for (int at = 0; length > 0; /*at*/) {
+            int v0 = bytes.getUnsignedByte(at);
+            char out;
+            switch (v0 >> 4) {
+                case 0x00: case 0x01: case 0x02: case 0x03:
+                case 0x04: case 0x05: case 0x06: case 0x07: {
+                    // 0XXXXXXX -- single-byte encoding
+                    length--;
+                    if (v0 == 0) {
+                        // A single zero byte is illegal.
+                        return throwBadUtf8(v0, at);
+                    }
+                    out = (char) v0;
+                    at++;
+                    break;
+                }
+                case 0x0c: case 0x0d: {
+                    // 110XXXXX -- two-byte encoding
+                    length -= 2;
+                    if (length < 0) {
+                        return throwBadUtf8(v0, at);
+                    }
+                    int v1 = bytes.getUnsignedByte(at + 1);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
+                    if ((value != 0) && (value < 0x80)) {
+                        /*
+                         * This should have been represented with
+                         * one-byte encoding.
+                         */
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    out = (char) value;
+                    at += 2;
+                    break;
+                }
+                case 0x0e: {
+                    // 1110XXXX -- three-byte encoding
+                    length -= 3;
+                    if (length < 0) {
+                        return throwBadUtf8(v0, at);
+                    }
+                    int v1 = bytes.getUnsignedByte(at + 1);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    int v2 = bytes.getUnsignedByte(at + 2);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v2, at + 2);
+                    }
+                    int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
+                        (v2 & 0x3f);
+                    if (value < 0x800) {
+                        /*
+                         * This should have been represented with one- or
+                         * two-byte encoding.
+                         */
+                        return throwBadUtf8(v2, at + 2);
+                    }
+                    out = (char) value;
+                    at += 3;
+                    break;
+                }
+                default: {
+                    // 10XXXXXX, 1111XXXX -- illegal
+                    return throwBadUtf8(v0, at);
+                }
+            }
+            chars[outAt] = out;
+            outAt++;
+        }
+
+        return new String(chars, 0, outAt);
+    }
+
+    /**
+     * Helper for {@link #utf8BytesToString}, which throws the right
+     * exception for a bogus utf-8 byte.
+     * 
+     * @param value the byte value
+     * @param offset the file offset
+     * @return never
+     * @throws IllegalArgumentException always thrown
+     */
+    private static String throwBadUtf8(int value, int offset) {
+        throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
+                                           " at offset " + Hex.u4(offset));
+    }
+
+    /**
+     * Constructs an instance from a <code>String</code>.
+     * 
+     * @param string non-null; the UTF-8 value as a string
+     */
+    public CstUtf8(String string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        this.string = string.intern();
+        this.bytes = new ByteArray(stringToUtf8Bytes(string));
+    }
+
+    /**
+     * Constructs an instance from some UTF-8 bytes.
+     * 
+     * @param bytes non-null; array of the UTF-8 bytes
+     */
+    public CstUtf8(ByteArray bytes) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        this.bytes = bytes;
+        this.string = utf8BytesToString(bytes).intern();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstUtf8)) {
+            return false;
+        }
+
+        return string.equals(((CstUtf8) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return string.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return string.compareTo(((CstUtf8) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "utf8{\"" + toHuman() + "\"}";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "utf8";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        int len = string.length();
+        StringBuilder sb = new StringBuilder(len * 3 / 2);
+
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            if ((c >= ' ') && (c < 0x7f)) {
+                if ((c == '\'') || (c == '\"') || (c == '\\')) {
+                    sb.append('\\');
+                }
+                sb.append(c);
+            } else if (c <= 0x7f) {
+                switch (c) {
+                    case '\n': sb.append("\\n"); break;
+                    case '\r': sb.append("\\r"); break;
+                    case '\t': sb.append("\\t"); break;
+                    default: {
+                        /*
+                         * Represent the character as an octal escape.
+                         * If the next character is a valid octal
+                         * digit, disambiguate by using the
+                         * three-digit form.
+                         */
+                        char nextChar =
+                            (i < (len - 1)) ? string.charAt(i + 1) : 0;
+                        boolean displayZero = 
+                            (nextChar >= '0') && (nextChar <= '7');
+                        sb.append('\\');
+                        for (int shift = 6; shift >= 0; shift -= 3) {
+                            char outChar = (char) (((c >> shift) & 7) + '0');
+                            if ((outChar != '0') || displayZero) {
+                                sb.append(outChar);
+                                displayZero = true;
+                            }
+                        }
+                        if (! displayZero) {
+                            // Ironic edge case: The original value was 0.
+                            sb.append('0');
+                        }
+                        break;
+                    }
+                }
+            } else {
+                sb.append("\\u");
+                sb.append(Character.forDigit(c >> 12, 16));
+                sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
+                sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
+                sb.append(Character.forDigit(c & 0x0f, 16));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the value as a human-oriented string, surrounded by double
+     * quotes.
+     * 
+     * @return non-null; the quoted string
+     */
+    public String toQuoted() {
+        return '\"' + toHuman() + '\"';
+    }
+
+    /**
+     * Gets the value as a human-oriented string, surrounded by double
+     * quotes, but ellipsizes the result if it is longer than the given
+     * maximum length
+     * 
+     * @param maxLength &gt;= 5; the maximum length of the string to return
+     * @return non-null; the quoted string
+     */
+    public String toQuoted(int maxLength) {
+        String string = toHuman();
+        int length = string.length();
+        String ellipses;
+
+        if (length <= (maxLength - 2)) {
+            ellipses = "";
+        } else {
+            string = string.substring(0, maxLength - 5);
+            ellipses = "...";
+        }
+
+        return '\"' + string + ellipses + '\"';
+    }
+
+    /**
+     * Gets the UTF-8 value as a string.
+     * The returned string is always already interned.
+     * 
+     * @return non-null; the UTF-8 value as a string
+     */
+    public String getString() {
+        return string;
+    }
+
+    /**
+     * Gets the UTF-8 value as UTF-8 encoded bytes.
+     * 
+     * @return non-null; an array of the UTF-8 bytes
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the size of this instance as UTF-8 code points. That is,
+     * get the number of bytes in the UTF-8 encoding of this instance.
+     * 
+     * @return &gt;= 0; the UTF-8 size
+     */
+    public int getUtf8Size() {
+        return bytes.size();
+    }
+
+    /**
+     * Gets the size of this instance as UTF-16 code points. That is,
+     * get the number of 16-bit chars in the UTF-16 encoding of this
+     * instance. This is the same as the <code>length</code> of the
+     * Java <code>String</code> representation of this instance.
+     * 
+     * @return &gt;= 0; the UTF-16 size
+     */
+    public int getUtf16Size() {
+        return string.length();
+    }   
+}
diff --git a/dx/src/com/android/dx/rop/cst/StdConstantPool.java b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
new file mode 100644
index 0000000..6979102
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Standard implementation of {@link ConstantPool}, which directly stores
+ * an array of {@link Constant} objects and can be made immutable.
+ */
+public final class StdConstantPool
+        extends MutabilityControl implements ConstantPool {
+    /** non-null; array of entries */
+    private final Constant[] entries;
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the pool; this corresponds to the
+     * class file field <code>constant_pool_count</code>, and is in fact
+     * always at least one more than the actual size of the constant pool,
+     * as element <code>0</code> is always invalid.
+     */
+    public StdConstantPool(int size) {
+        super(size > 1);
+
+        if (size < 1) {
+            throw new IllegalArgumentException("size < 1");
+        }
+
+        entries = new Constant[size];
+    }
+
+    /** {@inheritDoc} */
+    public int size() {
+        return entries.length;
+    }
+
+    /** {@inheritDoc} */
+    public Constant getOrNull(int n) {
+        try {
+            return entries[n];
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwInvalid(n);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Constant get0Ok(int n) {
+        if (n == 0) {
+            return null;
+        }
+
+        return get(n);
+    }
+
+    /** {@inheritDoc} */
+    public Constant get(int n) {
+        try {
+            Constant result = entries[n];
+
+            if (result == null) {
+                throwInvalid(n);
+            }
+
+            return result;
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwInvalid(n);
+        }
+    }
+
+    /**
+     * Sets the entry at the given index.
+     * 
+     * @param n &gt;= 1, &lt; size(); which entry
+     * @param cst null-ok; the constant to store
+     */
+    public void set(int n, Constant cst) {
+        throwIfImmutable();
+
+        boolean cat2 = (cst != null) && cst.isCategory2();
+
+        if (n < 1) {
+            throw new IllegalArgumentException("n < 1");
+        }
+
+        if (cat2) {
+            // Storing a category-2 entry nulls out the next index.
+            if (n == (entries.length - 1)) {
+                throw new IllegalArgumentException("(n == size - 1) && " +
+                                                   "cst.isCategory2()");
+            }
+            entries[n + 1] = null;
+        }
+
+        if ((cst != null) && (entries[n] == null)) {
+            /*
+             * Overwriting the second half of a category-2 entry nulls out
+             * the first half.
+             */
+            Constant prev = entries[n - 1];
+            if ((prev != null) && prev.isCategory2()) {
+                entries[n - 1] = null;
+            }
+        }
+
+        entries[n] = cst;
+    }
+
+    /**
+     * Throws the right exception for an invalid cpi.
+     * 
+     * @param idx the bad cpi
+     * @return never
+     * @throws ExceptionWithContext always thrown
+     */
+    private static Constant throwInvalid(int idx) {
+        throw new ExceptionWithContext("invalid constant pool index " +
+                                       Hex.u2(idx));
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/TypedConstant.java b/dx/src/com/android/dx/rop/cst/TypedConstant.java
new file mode 100644
index 0000000..54472b0
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/TypedConstant.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants which implement {@link TypeBearer}.
+ */
+public abstract class TypedConstant
+        extends Constant implements TypeBearer {
+    /**
+     * {@inheritDoc}
+     * 
+     * This implentation always returns <code>this</code>.
+     */
+    public final TypeBearer getFrameType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicType() {
+        return getType().getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicFrameType() {
+        return getType().getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean isConstant() {
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/Zeroes.java b/dx/src/com/android/dx/rop/cst/Zeroes.java
new file mode 100644
index 0000000..3379b6c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Zeroes.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Utility for turning types into zeroes.
+ */
+public final class Zeroes {
+    /**
+     * This class is uninstantiable.
+     */
+    private Zeroes() {
+        // This space intentionally left blank.
+    }
+     
+    /**
+     * Gets the "zero" (or <code>null</code>) value for the given type.
+     * 
+     * @param type non-null; the type in question
+     * @return non-null; its "zero" value
+     */
+    public static Constant zeroFor(Type type) {
+        switch (type.getBasicType()) {
+            case Type.BT_BOOLEAN: return CstBoolean.VALUE_FALSE;
+            case Type.BT_BYTE:    return CstByte.VALUE_0;
+            case Type.BT_CHAR:    return CstChar.VALUE_0;
+            case Type.BT_DOUBLE:  return CstDouble.VALUE_0;
+            case Type.BT_FLOAT:   return CstFloat.VALUE_0;
+            case Type.BT_INT:     return CstInteger.VALUE_0;
+            case Type.BT_LONG:    return CstLong.VALUE_0;
+            case Type.BT_SHORT:   return CstShort.VALUE_0;
+            case Type.BT_OBJECT:  return CstKnownNull.THE_ONE;
+            default: {
+                throw new UnsupportedOperationException("no zero for type: " +
+                        type.toHuman());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/package.html b/dx/src/com/android/dx/rop/cst/package.html
new file mode 100644
index 0000000..c784d16
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/package.html
@@ -0,0 +1,9 @@
+<body>
+<p>Interfaces and implementation of things related to the constant pool.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.type</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/package-info.java b/dx/src/com/android/dx/rop/package-info.java
new file mode 100644
index 0000000..97fe9de
--- /dev/null
+++ b/dx/src/com/android/dx/rop/package-info.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop;
+
+/**
+ * <h1>An Introduction to Rop Form</h1>
+ *
+ * This package contains classes associated with dx's <code>Rop</code>
+ * intermediate form.<p>
+ *
+ * The Rop form is intended to represent the instructions and the control-flow
+ * graph in a reasonably programmatically useful form while closely mirroring
+ * the dex instruction set.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <ul>
+ * <li> {@link RopMethod}, the representation of an individual method
+ * <li> {@link BasicBlock} and its per-method container, {@link BasicBlockList},
+ * the representation of control flow elements.
+ * <li> {@link Insn} and its subclasses along with its per-basic block
+ * container {@link InsnList}. <code>Insn</code> instances represent
+ * individual instructions in the abstract register machine.
+ * <li> {@link RegisterSpec} and its container {@link RegisterSpecList}. A
+ * register spec encodes register number, register width, type information,
+ * and potentially local variable information as well for instruction sources
+ * and results.
+ * <li> {@link Rop} instances represent opcodes in the abstract machine. Many
+ * <code>Rop</code> instances are singletons defined in static fields in
+ * {@link Rops}. The rest are constructed dynamically using static methods
+ * in <code>Rops</code>
+ * <li> {@link RegOps} lists numeric constants for the opcodes
+ * <li> {@link Constant} and its subclasses represent constant data values
+ * that opcodes may refer to.
+ * <li> {@link Type} instances represent the core data types that can be
+ * handled by the abstract machine.
+ * <li> The {@link TypeBearer} interface is implemented by classes that
+ * represent a core data type, but may also have secondary information
+ * (such as constant value) associated with them.
+ * <ul>
+ *
+ * <h2>Control-Flow Graph</h2>
+ *
+ * Each method is separated into a list of basic blocks. For the most part,
+ * basic blocks are referred to by a positive integer
+ * {@link BasicBlock#getLabel label}, which is always unique per method. The
+ * label value is typically derived from a bytecode address from the source
+ * bytecode. Blocks that don't originate directly from source bytecode have
+ * labels generated for them in a mostly arbitrary order.<p>
+ *
+ * Blocks are referred to by their label, for the most part, because <code>
+ * BasicBlock</code> instances are immutable and thus any modification to
+ * the control flow graph or the instruction list results in replacement
+ * instances (with identical labels) being created.<p>
+ *
+ * A method has a single {@link RopMethod#getFirstLabel entry block} and 0
+ * to N {@link RopMethod#getExitPredecessors exit predecessor blocks} which
+ * will return. All blocks that are not the entry block will have at least
+ * one predecessor (or are unreachable and should be removed from the block
+ * list). All blocks that are not exit predecessors must have at least one
+ * successor.<p>
+ *
+ * Since all blocks must branch, all blocks must have, as their final
+ * instruction, an instruction whose opcode has a {@link Rop#getBranchingness
+ * branchingness} other than {@link Rop.BRANCH_NONE}. Furthermore, branching
+ * instructions may only be the final instruction in any basic block. If
+ * no other terminating opcode is appropriate, use a {@link Rops#GOTO GOTO}.<p>
+ *
+ * Typically a block will have a {@link BasicBlock#getPrimarySuccessor
+ * primary successor} which distinguishes a particular control flow path.
+ * For {Rops#isCallLike}call or call-like} opcodes, this is the path taken
+ * in the non-exceptional case, where all other successors represent
+ * various exception paths. For comparison operators such as
+ * {@link Rops#IF_EQZ_INT}, the primary successor represents the path taken
+ * if the <b>condition evaluates to false</b>. For {@link SwitchInsn switch
+ * instructions}, the primary successor is the default case.<p>
+ *
+ * A basic block's successor list is ordered and may refer to unique labels
+ * multiple times. For example, if a switch statement contains multiple case
+ * statements for the same code path, a single basic block will likely
+ * appear in the successor list multiple times. In general, the
+ * significance of the successor list's order (like the significance of
+ * the primary successor) is a property of the final instruction of the basic
+ * block. A basic block containing a {@link ThrowingInsn}, for example, has
+ * its successor list in an order identical to the
+ * {@link ThrowingInsn#getCatches} instruction's catches list, with the
+ * primary successor (the no-exception case) listed at the end.
+ *
+ * It is legal for a basic block to have no primary successor. An obvious
+ * example of this is a block that terminates in a {@link Rops#THROW throw}
+ * instruction where a catch block exists inside the current method for that
+ * exception class. Since the only possible path is the exception path, only
+ * the exception path (which cannot be a primary successor) is a successor.
+ * An example of this is shown in <code>dx/tests/092-ssa-cfg-edge-cases</code>.
+ *
+ * <h2>Rop Instructions</h2>
+ *
+ * <h3>move-result and move-result-pseudo</h3>
+ *
+ * An instruction that may throw an exception may not specify a result. This
+ * is necessary because the result register will not be assigned to if an
+ * exception occurs while processing said instruction and a result assignment
+ * may not occur. Since result assignments only occur in the non-exceptional
+ * case,  the result assignments for throwing instructions can be said to occur
+ * at the beginning of the primary successor block rather than at the end of
+ * the current block. The Rop form represents the result assignments this way.
+ * Throwing instructions may not directly specify results. Instead, result
+ * assignments are represented by {@link
+ * Rops#MOVE_RESULT move-result} or {@link Rops#MOVE_RESULT_PSEUDO
+ * move-result-pseudo} instructions at the top of the primary successor block.
+ *
+ * Only a single <code>move-result</code> or <code>move-result-pseudo</code>
+ * may exist in any block and it must be exactly the first instruction in the
+ * block.
+ *
+ * A <code>move-result</code> instruction is used for the results of call-like
+ * instructions. If the value produced by a <code>move-result</code> is not
+ * used by the method, it may be eliminated as dead code.
+ *
+ * A <code>move-result-pseudo</code> instruction is used for the results of
+ * non-call-like throwing instructions. It may never be considered dead code
+ * since the final dex instruction will always indicate a result register.
+ * If a required <code>move-result-pseudo</code> instruction is not found
+ * during conversion to dex bytecode, an exception will be thrown.
+ *
+ * <h3>move-exception</h3>
+ *
+ * A {@link RegOps.MOVE_EXCEPTION move-exception} instruction may appear at
+ * the start of a catch block, and represents the obtaining of the thrown
+ * exception instance. It may only occur as the first instruction in a
+ * basic block, and any basic block in which it occurs must be reachable only
+ * as an exception successor.
+ *
+ * <h3>move-param</h3>
+ *
+ * A {@link RegOps.MOVE_PARAM move-param} instruction represents a method
+ * parameter. Every <code>move-param</code> instruction is a
+ * {@link PlainCstInsn}. The index of the method parameter they refer to is
+ * carried as the {@link CstInteger integer constant} associated with the
+ * instruction.
+ *
+ * Any number of <code>move-param</code> instructions referring to the same
+ * parameter index may be included in a method's instruction lists. They
+ * have no restrictions on placement beyond those of any other
+ * {@link Rop.BRANCH_NONE} instruction. Note that the SSA optimizer arranges the
+ * parameter assignments to align with the dex bytecode calling conventions.
+ * With parameter assignments so arranged, the
+ * {@link com.android.dx.dex.code.RopTranslator} sees Rop <code>move-param</code>
+ * instructions as unnecessary in dex form and eliminates them.
+ *
+ * <h3>mark-local</h3>
+ *
+ * A {@link RegOps.MARK_LOCAL mark-local} instruction indicates that a local
+ * variable becomes live in a specified register specified register for the
+ * purposes of debug information. A <code>mark-local</code> instruction has
+ * a single source (the register which will now be considered a local variable)
+ * and no results. The instruction has no side effect.<p>
+ *
+ * In a typical case, a local variable's lifetime begins with an
+ * assignment. The local variable whose information is present in a result's
+ * {@link RegisterSpec#getLocalItem LocalItem} is considered to begin (or move
+ * between registers) when the instruction is executed.<p>
+ *
+ * However, sometimes a local variable can begin its life or move without
+ * an assignment occurring. A common example of this is occurs in the Rop
+ * representation of the following code:<p>
+ *
+ * <code>
+ * try {
+ *     Object foo = null;
+ *     foo = new Object();
+ * } catch (Throwable ex) {
+ * }
+ * </code>
+ *
+ * An object's initialization occurs in two steps. First, a <code>new-instance
+ * </code> instruction is executed, whose result is stored in a register.
+ * However, that register can not yet be considered to contain "foo". That's
+ * because the instance's constructor method must be called via an
+ * <code>invoke</code> instruction. The constructor method, however, may
+ * throw an exception. And if an exception occurs, then "foo" should remain
+ * null. So "foo" becomes the value of the result of the <code>new-instance
+ * </code> instruction after the (void) constructor method is invoked and
+ * returns successfully. In such a case, a <code>mark-local</code> will
+ * typically occur at the beginning of the primary successor block following
+ * the invocation to the constructor.
+ */
diff --git a/dx/src/com/android/dx/rop/type/Prototype.java b/dx/src/com/android/dx/rop/type/Prototype.java
new file mode 100644
index 0000000..a6ee742
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Prototype.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a method decriptor. Instances of this class are
+ * generally interned and may be usefully compared with each other
+ * using <code>==</code>.
+ */
+public final class Prototype implements Comparable<Prototype> {
+    /** non-null; intern table mapping string descriptors to instances */
+    private static final HashMap<String, Prototype> internTable =
+        new HashMap<String, Prototype>(500);
+
+    /** non-null; method descriptor */
+    private final String descriptor;
+
+    /** non-null; return type */
+    private final Type returnType;
+
+    /** non-null; list of parameter types */
+    private final StdTypeList parameterTypes;
+
+    /** null-ok; list of parameter frame types, if calculated */
+    private StdTypeList parameterFrameTypes;
+
+    /**
+     * Returns the unique instance corresponding to the 
+     * given method descriptor. See vmspec-2 sec4.3.3 for details on the
+     * field descriptor syntax.
+     * 
+     * @param descriptor non-null; the descriptor
+     * @return non-null; the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Prototype intern(String descriptor) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        Prototype result = internTable.get(descriptor);
+        if (result != null) {
+            return result;
+        }
+
+        Type[] params = makeParameterArray(descriptor);
+        int paramCount = 0;
+        int at = 1;
+
+        for (;;) {
+            int startAt = at;
+            char c = descriptor.charAt(at);
+            if (c == ')') {
+                at++;
+                break;
+            }
+
+            // Skip array markers.
+            while (c == '[') {
+                at++;
+                c = descriptor.charAt(at);
+            }
+
+            if (c == 'L') {
+                // It looks like the start of a class name; find the end.
+                int endAt = descriptor.indexOf(';', at);
+                if (endAt == -1) {
+                    throw new IllegalArgumentException("bad descriptor");
+                }
+                at = endAt + 1;
+            } else {
+                at++;
+            }
+
+            params[paramCount] =
+                Type.intern(descriptor.substring(startAt, at));
+            paramCount++;
+        }
+
+        Type returnType = Type.internReturnType(descriptor.substring(at));
+        StdTypeList parameterTypes = new StdTypeList(paramCount);
+
+        for (int i = 0; i < paramCount; i++) {
+            parameterTypes.set(i, params[i]);
+        }
+
+        result = new Prototype(descriptor, returnType, parameterTypes);
+        return putIntern(result);
+    }
+
+    /**
+     * Helper for {@link #intern} which returns an empty array to
+     * populate with parsed parameter types, and which also ensures
+     * that there is a '(' at the start of the descriptor and a
+     * single ')' somewhere before the end.
+     * 
+     * @param descriptor non-null; the descriptor string
+     * @return non-null; array large enough to hold all parsed parameter
+     * types, but which is likely actually larger than needed
+     */
+    private static Type[] makeParameterArray(String descriptor) {
+        int length = descriptor.length();
+
+        if (descriptor.charAt(0) != '(') {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        /*
+         * This is a cheesy way to establish an upper bound on the
+         * number of parameters: Just count capital letters.
+         */
+        int closeAt = 0;
+        int maxParams = 0;
+        for (int i = 1; i < length; i++) {
+            char c = descriptor.charAt(i);
+            if (c == ')') {
+                closeAt = i;
+                break;
+            }
+            if ((c >= 'A') && (c <= 'Z')) {
+                maxParams++;
+            }
+        }
+
+        if ((closeAt == 0) || (closeAt == (length - 1))) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        if (descriptor.indexOf(')', closeAt + 1) != -1) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        return new Type[maxParams];
+    }
+
+    /**
+     * Interns an instance, adding to the descriptor as necessary based
+     * on the given definer, name, and flags. For example, an init
+     * method has an uninitialized object of type <code>definer</code>
+     * as its first argument.
+     * 
+     * @param descriptor non-null; the descriptor string
+     * @param definer non-null; class the method is defined on
+     * @param isStatic whether this is a static method
+     * @param isInit whether this is an init method
+     * @return non-null; the interned instance
+     */
+    public static Prototype intern(String descriptor, Type definer,
+            boolean isStatic, boolean isInit) {
+        Prototype base = intern(descriptor);
+
+        if (isStatic) {
+            return base;
+        }
+
+        if (isInit) {
+            definer = definer.asUninitialized(Integer.MAX_VALUE);
+        }
+
+        return base.withFirstParameter(definer);
+    }
+
+    /**
+     * Interns an instance which consists of the given number of
+     * <code>int</code>s along with the given return type
+     * 
+     * @param returnType non-null; the return type
+     * @param count &gt; 0; the number of elements in the prototype
+     * @return non-null; the interned instance
+     */
+    public static Prototype internInts(Type returnType, int count) {
+        // Make the descriptor...
+
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append('(');
+
+        for (int i = 0; i < count; i++) {
+            sb.append('I');
+        }
+
+        sb.append(')');
+        sb.append(returnType.getDescriptor());
+
+        // ...and intern it.
+        return intern(sb.toString());
+    }
+
+    /**
+     * Constructs an instance. This is a private constructor; use one
+     * of the public static methods to get instances.
+     * 
+     * @param descriptor non-null; the descriptor string
+     */
+    private Prototype(String descriptor, Type returnType,
+            StdTypeList parameterTypes) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if (returnType == null) {
+            throw new NullPointerException("returnType == null");
+        }
+
+        if (parameterTypes == null) {
+            throw new NullPointerException("parameterTypes == null");
+        }
+
+        this.descriptor = descriptor;
+        this.returnType = returnType;
+        this.parameterTypes = parameterTypes;
+        this.parameterFrameTypes = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            /*
+             * Since externally-visible instances are interned, this
+             * check helps weed out some easy cases.
+             */
+            return true;
+        }
+
+        if (!(other instanceof Prototype)) {
+            return false;
+        }
+
+        return descriptor.equals(((Prototype) other).descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Prototype other) {
+        if (this == other) {
+            return 0;
+        }
+        
+        /*
+         * The return type is the major order, and then args in order,
+         * and then the shorter list comes first (similar to string
+         * sorting).
+         */
+
+        int result = returnType.compareTo(other.returnType);
+
+        if (result != 0) {
+            return result;
+        }
+
+        int thisSize = parameterTypes.size();
+        int otherSize = other.parameterTypes.size();
+        int size = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < size; i++) {
+            Type thisType = parameterTypes.get(i);
+            Type otherType = other.parameterTypes.get(i);
+
+            result = thisType.compareTo(otherType);
+
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the descriptor string.
+     * 
+     * @return non-null; the descriptor
+     */
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the return type.
+     * 
+     * @return non-null; the return type
+     */
+    public Type getReturnType() {
+        return returnType;
+    }
+
+    /**
+     * Gets the list of parameter types.
+     * 
+     * @return non-null; the list of parameter types
+     */
+    public StdTypeList getParameterTypes() {
+        return parameterTypes;
+    }
+
+    /**
+     * Gets the list of frame types corresponding to the list of parameter
+     * types. The difference between the two lists (if any) is that all
+     * "intlike" types (see {@link Type#isIntlike}) are replaced by
+     * {@link Type#INT}.
+     * 
+     * @return non-null; the list of parameter frame types
+     */
+    public StdTypeList getParameterFrameTypes() {
+        if (parameterFrameTypes == null) {
+            int sz = parameterTypes.size();
+            StdTypeList list = new StdTypeList(sz);
+            boolean any = false;
+            for (int i = 0; i < sz; i++) {
+                Type one = parameterTypes.get(i);
+                if (one.isIntlike()) {
+                    any = true;
+                    one = Type.INT;
+                }
+                list.set(i, one);
+            }
+            parameterFrameTypes = any ? list : parameterTypes;
+        }
+
+        return parameterFrameTypes;
+    }
+
+    /**
+     * Returns a new interned instance, which is the same as this instance,
+     * except that it has an additional parameter prepended to the original's
+     * argument list.
+     * 
+     * @param param non-null; the new first parameter
+     * @return non-null; an appropriately-constructed instance
+     */
+    public Prototype withFirstParameter(Type param) {
+        String newDesc = "(" + param.getDescriptor() + descriptor.substring(1);
+        StdTypeList newParams = parameterTypes.withFirst(param);
+
+        newParams.setImmutable();
+
+        Prototype result =
+            new Prototype(newDesc, returnType, newParams);
+
+        return putIntern(result);
+    }
+
+    /**
+     * Puts the given instance in the intern table if it's not already
+     * there. If a conflicting value is already in the table, then leave it.
+     * Return the interned value.
+     * 
+     * @param desc non-null; instance to make interned
+     * @return non-null; the actual interned object
+     */
+    private static Prototype putIntern(Prototype desc) {
+        synchronized (internTable) {
+            String descriptor = desc.getDescriptor();
+            Prototype already = internTable.get(descriptor);
+            if (already != null) {
+                return already;
+            }
+            internTable.put(descriptor, desc);
+            return desc;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/StdTypeList.java b/dx/src/com/android/dx/rop/type/StdTypeList.java
new file mode 100644
index 0000000..a4c2d44
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/StdTypeList.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link TypeList}.
+ */
+public final class StdTypeList
+        extends FixedSizeList implements TypeList {
+    /** non-null; no-element instance */
+    public static final StdTypeList EMPTY = new StdTypeList(0);
+
+    /** non-null; the list <code>[int]</code> */
+    public static final StdTypeList INT = StdTypeList.make(Type.INT);
+
+    /** non-null; the list <code>[long]</code> */
+    public static final StdTypeList LONG = StdTypeList.make(Type.LONG);
+
+    /** non-null; the list <code>[float]</code> */
+    public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT);
+
+    /** non-null; the list <code>[double]</code> */
+    public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE);
+
+    /** non-null; the list <code>[Object]</code> */
+    public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT);
+
+    /** non-null; the list <code>[ReturnAddress]</code> */
+    public static final StdTypeList RETURN_ADDRESS
+            = StdTypeList.make(Type.RETURN_ADDRESS);
+
+    /** non-null; the list <code>[Throwable]</code> */
+    public static final StdTypeList THROWABLE =
+        StdTypeList.make(Type.THROWABLE);
+
+    /** non-null; the list <code>[int, int]</code> */
+    public static final StdTypeList INT_INT =
+        StdTypeList.make(Type.INT, Type.INT);
+
+    /** non-null; the list <code>[long, long]</code> */
+    public static final StdTypeList LONG_LONG =
+        StdTypeList.make(Type.LONG, Type.LONG);
+
+    /** non-null; the list <code>[float, float]</code> */
+    public static final StdTypeList FLOAT_FLOAT =
+        StdTypeList.make(Type.FLOAT, Type.FLOAT);
+
+    /** non-null; the list <code>[double, double]</code> */
+    public static final StdTypeList DOUBLE_DOUBLE =
+        StdTypeList.make(Type.DOUBLE, Type.DOUBLE);
+
+    /** non-null; the list <code>[Object, Object]</code> */
+    public static final StdTypeList OBJECT_OBJECT =
+        StdTypeList.make(Type.OBJECT, Type.OBJECT);
+
+    /** non-null; the list <code>[int, Object]</code> */
+    public static final StdTypeList INT_OBJECT =
+        StdTypeList.make(Type.INT, Type.OBJECT);
+
+    /** non-null; the list <code>[long, Object]</code> */
+    public static final StdTypeList LONG_OBJECT =
+        StdTypeList.make(Type.LONG, Type.OBJECT);
+
+    /** non-null; the list <code>[float, Object]</code> */
+    public static final StdTypeList FLOAT_OBJECT =
+        StdTypeList.make(Type.FLOAT, Type.OBJECT);
+
+    /** non-null; the list <code>[double, Object]</code> */
+    public static final StdTypeList DOUBLE_OBJECT =
+        StdTypeList.make(Type.DOUBLE, Type.OBJECT);
+
+    /** non-null; the list <code>[long, int]</code> */
+    public static final StdTypeList LONG_INT =
+        StdTypeList.make(Type.LONG, Type.INT);
+
+    /** non-null; the list <code>[int[], int]</code> */
+    public static final StdTypeList INTARR_INT =
+        StdTypeList.make(Type.INT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[long[], int]</code> */
+    public static final StdTypeList LONGARR_INT =
+        StdTypeList.make(Type.LONG_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[float[], int]</code> */
+    public static final StdTypeList FLOATARR_INT =
+        StdTypeList.make(Type.FLOAT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[double[], int]</code> */
+    public static final StdTypeList DOUBLEARR_INT =
+        StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[Object[], int]</code> */
+    public static final StdTypeList OBJECTARR_INT =
+        StdTypeList.make(Type.OBJECT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[boolean[], int]</code> */
+    public static final StdTypeList BOOLEANARR_INT =
+        StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[byte[], int]</code> */
+    public static final StdTypeList BYTEARR_INT =
+        StdTypeList.make(Type.BYTE_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[char[], int]</code> */
+    public static final StdTypeList CHARARR_INT =
+        StdTypeList.make(Type.CHAR_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[short[], int]</code> */
+    public static final StdTypeList SHORTARR_INT =
+        StdTypeList.make(Type.SHORT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[int, int[], int]</code> */
+    public static final StdTypeList INT_INTARR_INT =
+        StdTypeList.make(Type.INT, Type.INT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[long, long[], int]</code> */
+    public static final StdTypeList LONG_LONGARR_INT =
+        StdTypeList.make(Type.LONG, Type.LONG_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[float, float[], int]</code> */
+    public static final StdTypeList FLOAT_FLOATARR_INT =
+        StdTypeList.make(Type.FLOAT, Type.FLOAT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[double, double[], int]</code> */
+    public static final StdTypeList DOUBLE_DOUBLEARR_INT =
+        StdTypeList.make(Type.DOUBLE, Type.DOUBLE_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[Object, Object[], int]</code> */
+    public static final StdTypeList OBJECT_OBJECTARR_INT =
+        StdTypeList.make(Type.OBJECT, Type.OBJECT_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[int, boolean[], int]</code> */
+    public static final StdTypeList INT_BOOLEANARR_INT =
+        StdTypeList.make(Type.INT, Type.BOOLEAN_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[int, byte[], int]</code> */
+    public static final StdTypeList INT_BYTEARR_INT =
+        StdTypeList.make(Type.INT, Type.BYTE_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[int, char[], int]</code> */
+    public static final StdTypeList INT_CHARARR_INT =
+        StdTypeList.make(Type.INT, Type.CHAR_ARRAY, Type.INT);
+
+    /** non-null; the list <code>[int, short[], int]</code> */
+    public static final StdTypeList INT_SHORTARR_INT =
+        StdTypeList.make(Type.INT, Type.SHORT_ARRAY, Type.INT);
+
+    /**
+     * Makes a single-element instance.
+     * 
+     * @param type non-null; the element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type) {
+        StdTypeList result = new StdTypeList(1);
+        result.set(0, type);
+        return result;
+    }
+
+    /**
+     * Makes a two-element instance.
+     * 
+     * @param type0 non-null; the first element
+     * @param type1 non-null; the second element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1) {
+        StdTypeList result = new StdTypeList(2);
+        result.set(0, type0);
+        result.set(1, type1);
+        return result;
+    }
+
+    /**
+     * Makes a three-element instance.
+     * 
+     * @param type0 non-null; the first element
+     * @param type1 non-null; the second element
+     * @param type2 non-null; the third element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1, Type type2) {
+        StdTypeList result = new StdTypeList(3);
+        result.set(0, type0);
+        result.set(1, type1);
+        result.set(2, type2);
+        return result;
+    }
+
+    /**
+     * Makes a four-element instance.
+     * 
+     * @param type0 non-null; the first element
+     * @param type1 non-null; the second element
+     * @param type2 non-null; the third element
+     * @param type3 non-null; the fourth element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1, Type type2,
+                                   Type type3) {
+        StdTypeList result = new StdTypeList(4);
+        result.set(0, type0);
+        result.set(1, type1);
+        result.set(2, type2);
+        result.set(3, type3);
+        return result;
+    }
+
+    /**
+     * Returns the given list as a comma-separated list of human forms. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     * 
+     * @param list non-null; the list to convert
+     * @return non-null; the human form
+     */
+    public static String toHuman(TypeList list) {
+        int size = list.size();
+
+        if (size == 0) {
+            return "<empty>";
+        }
+        
+        StringBuffer sb = new StringBuffer(100);
+
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(list.getType(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns a hashcode of the contents of the given list. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     * 
+     * @param list non-null; the list to inspect
+     * @return non-null; the hash code
+     */
+    public static int hashContents(TypeList list) {
+        int size = list.size();
+        int hash = 0;
+
+        for (int i = 0; i < size; i++) {
+            hash = (hash * 31) + list.getType(i).hashCode();
+        }
+
+        return hash;
+    }
+    
+    /**
+     * Compares the contents of the given two instances for equality. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     * 
+     * @param list1 non-null; one list to compare
+     * @param list2 non-null; another list to compare
+     * @return whether the two lists contain corresponding equal elements
+     */
+    public static boolean equalContents(TypeList list1, TypeList list2) {
+        int size = list1.size();
+
+        if (list2.size() != size) {
+            return false;
+        }
+
+        for (int i = 0; i < size; i++) {
+            if (! list1.getType(i).equals(list2.getType(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Compares the contents of the given two instances for ordering. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     * 
+     * @param list1 non-null; one list to compare
+     * @param list2 non-null; another list to compare
+     * @return the order of the two lists
+     */
+    public static int compareContents(TypeList list1, TypeList list2) {
+        int size1 = list1.size();
+        int size2 = list2.size();
+        int size = Math.min(size1, size2);
+
+        for (int i = 0; i < size; i++) {
+            int comparison = list1.getType(i).compareTo(list2.getType(i));
+            if (comparison != 0) {
+                return comparison;
+            }
+        }
+
+        if (size1 == size2) {
+            return 0;
+        } else if (size1 < size2) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+    
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public StdTypeList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType(int n) {
+        return get(n);
+    }
+
+    /** {@inheritDoc} */
+    public int getWordCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            result += get(i).getCategory();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList withAddedType(Type type) {
+        int sz = size();
+        StdTypeList result = new StdTypeList(sz + 1);
+
+        for (int i = 0; i < sz; i++) {
+            result.set0(i, get0(i));
+        }
+
+        result.set(sz, type);
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw <code>NullPointerException</code>.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return non-null; the indicated element
+     */
+    public Type get(int n) {
+        return (Type) get0(n);
+    }
+
+    /**
+     * Sets the type at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param type non-null; the type to store
+     */
+    public void set(int n, Type type) {
+        set0(n, type);
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that it has an additional type prepended to the
+     * original.
+     * 
+     * @param type non-null; the new first element
+     * @return non-null; an appropriately-constructed instance
+     */
+    public StdTypeList withFirst(Type type) {
+        int sz = size();
+        StdTypeList result = new StdTypeList(sz + 1);
+
+        result.set0(0, type);
+        for (int i = 0; i < sz; i++) {
+            result.set0(i + 1, getOrNull0(i));
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java
new file mode 100644
index 0000000..09ea2e2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a value type, such as may appear in a field, in a
+ * local, on a stack, or in a method descriptor. Instances of this
+ * class are generally interned and may be usefully compared with each
+ * other using <code>==</code>.
+ */
+public final class Type implements TypeBearer, Comparable<Type> {
+    /** non-null; intern table mapping string descriptors to instances */
+    private static final HashMap<String, Type> internTable = 
+        new HashMap<String, Type>(500);
+
+    /** basic type constant for <code>void</code> */
+    public static final int BT_VOID = 0;
+
+    /** basic type constant for <code>boolean</code> */
+    public static final int BT_BOOLEAN = 1;
+
+    /** basic type constant for <code>byte</code> */
+    public static final int BT_BYTE = 2;
+
+    /** basic type constant for <code>char</code> */
+    public static final int BT_CHAR = 3;
+
+    /** basic type constant for <code>double</code> */
+    public static final int BT_DOUBLE = 4;
+
+    /** basic type constant for <code>float</code> */
+    public static final int BT_FLOAT = 5;
+
+    /** basic type constant for <code>int</code> */
+    public static final int BT_INT = 6;
+
+    /** basic type constant for <code>long</code> */
+    public static final int BT_LONG = 7;
+
+    /** basic type constant for <code>short</code> */
+    public static final int BT_SHORT = 8;
+
+    /** basic type constant for <code>Object</code> */
+    public static final int BT_OBJECT = 9;
+
+    /** basic type constant for a return address */
+    public static final int BT_ADDR = 10;
+
+    /** count of basic type constants */
+    public static final int BT_COUNT = 11;
+
+    /** non-null; instance representing <code>boolean</code> */
+    public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN);
+
+    /** non-null; instance representing <code>byte</code> */
+    public static final Type BYTE = new Type("B", BT_BYTE);
+
+    /** non-null; instance representing <code>char</code> */
+    public static final Type CHAR = new Type("C", BT_CHAR);
+
+    /** non-null; instance representing <code>double</code> */
+    public static final Type DOUBLE = new Type("D", BT_DOUBLE);
+
+    /** non-null; instance representing <code>float</code> */
+    public static final Type FLOAT = new Type("F", BT_FLOAT);
+
+    /** non-null; instance representing <code>int</code> */
+    public static final Type INT = new Type("I", BT_INT);
+
+    /** non-null; instance representing <code>long</code> */
+    public static final Type LONG = new Type("J", BT_LONG);
+
+    /** non-null; instance representing <code>short</code> */
+    public static final Type SHORT = new Type("S", BT_SHORT);
+
+    /** non-null; instance representing <code>void</code> */
+    public static final Type VOID = new Type("V", BT_VOID);
+
+    /** non-null; instance representing a known-<code>null</code> */
+    public static final Type KNOWN_NULL = new Type("<null>", BT_OBJECT);
+
+    /** non-null; instance representing a subroutine return address */
+    public static final Type RETURN_ADDRESS = new Type("<addr>", BT_ADDR);
+
+    static {
+        /*
+         * Put all the primitive types into the intern table. This needs
+         * to happen before the array types below get interned.
+         */
+        putIntern(BOOLEAN);
+        putIntern(BYTE);
+        putIntern(CHAR);
+        putIntern(DOUBLE);
+        putIntern(FLOAT);
+        putIntern(INT);
+        putIntern(LONG);
+        putIntern(SHORT);
+        /*
+         * Note: VOID isn't put in the intern table, since it's special and
+         * shouldn't be found by a normal call to intern().
+         */
+    }
+
+    /**
+     * non-null; instance representing
+     * <code>java.lang.annotation.Annotation</code>
+     */
+    public static final Type ANNOTATION =
+        intern("Ljava/lang/annotation/Annotation;");
+
+    /** non-null; instance representing <code>java.lang.Class</code> */
+    public static final Type CLASS = intern("Ljava/lang/Class;");
+
+    /** non-null; instance representing <code>java.lang.Cloneable</code> */
+    public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;");
+
+    /** non-null; instance representing <code>java.lang.Object</code> */
+    public static final Type OBJECT = intern("Ljava/lang/Object;");
+
+    /** non-null; instance representing <code>java.io.Serializable</code> */
+    public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;");
+
+    /** non-null; instance representing <code>java.lang.String</code> */
+    public static final Type STRING = intern("Ljava/lang/String;");
+
+    /** non-null; instance representing <code>java.lang.Throwable</code> */
+    public static final Type THROWABLE = intern("Ljava/lang/Throwable;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Boolean</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type BOOLEAN_CLASS = intern("Ljava/lang/Boolean;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Byte</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type BYTE_CLASS = intern("Ljava/lang/Byte;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Character</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type CHARACTER_CLASS = intern("Ljava/lang/Character;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Double</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type DOUBLE_CLASS = intern("Ljava/lang/Double;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Float</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type FLOAT_CLASS = intern("Ljava/lang/Float;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Integer</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type INTEGER_CLASS = intern("Ljava/lang/Integer;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Long</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type LONG_CLASS = intern("Ljava/lang/Long;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Short</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type SHORT_CLASS = intern("Ljava/lang/Short;");
+
+    /**
+     * non-null; instance representing <code>java.lang.Void</code>; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type 
+     */
+    public static final Type VOID_CLASS = intern("Ljava/lang/Void;");
+
+    /** non-null; instance representing <code>boolean[]</code> */
+    public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType();
+
+    /** non-null; instance representing <code>byte[]</code> */
+    public static final Type BYTE_ARRAY = BYTE.getArrayType();
+
+    /** non-null; instance representing <code>char[]</code> */
+    public static final Type CHAR_ARRAY = CHAR.getArrayType();
+
+    /** non-null; instance representing <code>double[]</code> */
+    public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType();
+
+    /** non-null; instance representing <code>float[]</code> */
+    public static final Type FLOAT_ARRAY = FLOAT.getArrayType();
+
+    /** non-null; instance representing <code>int[]</code> */
+    public static final Type INT_ARRAY = INT.getArrayType();
+
+    /** non-null; instance representing <code>long[]</code> */
+    public static final Type LONG_ARRAY = LONG.getArrayType();
+
+    /** non-null; instance representing <code>Object[]</code> */
+    public static final Type OBJECT_ARRAY = OBJECT.getArrayType();
+
+    /** non-null; instance representing <code>short[]</code> */
+    public static final Type SHORT_ARRAY = SHORT.getArrayType();
+
+    /** non-null; field descriptor for the type */
+    private final String descriptor;
+
+    /**
+     * basic type corresponding to this type; one of the
+     * <code>BT_*</code> constants 
+     */
+    private final int basicType;
+
+    /**
+     * &gt;= -1; for an uninitialized type, bytecode index that this
+     * instance was allocated at; <code>Integer.MAX_VALUE</code> if it
+     * was an incoming uninitialized instance; <code>-1</code> if this
+     * is an <i>inititialized</i> instance 
+     */
+    private final int newAt;
+
+    /**
+     * null-ok; the internal-form class name corresponding to this type, if
+     * calculated; only valid if <code>this</code> is a reference type and
+     * additionally not a return address 
+     */
+    private String className;
+
+    /**
+     * null-ok; the type corresponding to an array of this type, if
+     * calculated 
+     */
+    private Type arrayType;
+
+    /**
+     * null-ok; the type corresponding to elements of this type, if
+     * calculated; only valid if <code>this</code> is an array type 
+     */
+    private Type componentType;
+
+    /**
+     * null-ok; the type corresponding to the initialized version of
+     * this type, if this instance is in fact an uninitialized type 
+     */
+    private Type initializedType;
+
+    /**
+     * Returns the unique instance corresponding to the type with the
+     * given descriptor. See vmspec-2 sec4.3.2 for details on the
+     * field descriptor syntax. This method does <i>not</i> allow
+     * <code>"V"</code> (that is, type <code>void</code>) as a valid
+     * descriptor.
+     * 
+     * @param descriptor non-null; the descriptor
+     * @return non-null; the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Type intern(String descriptor) {
+        Type result = internTable.get(descriptor);
+        if (result != null) {
+            return result;
+        }
+
+        char firstChar;
+        try {
+            firstChar = descriptor.charAt(0);
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("descriptor is empty");
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if (firstChar == '[') {
+            /*
+             * Recursively strip away array markers to get at the underlying
+             * type, and build back on to form the result.
+             */
+            result = intern(descriptor.substring(1));
+            return result.getArrayType();
+        }
+
+        /*
+         * If the first character isn't '[' and it wasn't found in the
+         * intern cache, then it had better be the descriptor for a class.
+         */
+
+        int length = descriptor.length();
+        if ((firstChar != 'L') ||
+            (descriptor.charAt(length - 1) != ';')) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        /*
+         * Validate the characters of the class name itself. Note that
+         * vmspec-2 does not have a coherent definition for valid
+         * internal-form class names, and the definition here is fairly
+         * liberal: A name is considered valid as long as it doesn't
+         * contain any of '[' ';' '.' '(' ')', and it has no more than one
+         * '/' in a row, and no '/' at either end.
+         */
+
+        int limit = (length - 1); // Skip the final ';'.
+        for (int i = 1; i < limit; i++) {
+            char c = descriptor.charAt(i);
+            switch (c) {
+                case '[':
+                case ';': 
+                case '.': 
+                case '(':
+                case ')': {
+                    throw new IllegalArgumentException("bad descriptor");
+                }
+                case '/': {
+                    if ((i == 1) ||
+                        (i == (length - 1)) ||
+                        (descriptor.charAt(i - 1) == '/')) {
+                        throw new IllegalArgumentException("bad descriptor");
+                    }
+                    break;
+                }
+            }
+        }
+
+        result = new Type(descriptor, BT_OBJECT);
+        return putIntern(result);
+    }
+
+    /**
+     * Returns the unique instance corresponding to the type with the
+     * given descriptor, allowing <code>"V"</code> to return the type
+     * for <code>void</code>. Other than that one caveat, this method
+     * is identical to {@link #intern}.
+     * 
+     * @param descriptor non-null; the descriptor
+     * @return non-null; the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Type internReturnType(String descriptor) {
+        try {
+            if (descriptor.equals("V")) {
+                // This is the one special case where void may be returned.
+                return VOID;
+            }
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("descriptor == null");
+        }
+
+        return intern(descriptor);
+    }
+
+    /**
+     * Returns the unique instance corresponding to the type of the
+     * class with the given name. Calling this method is equivalent to
+     * calling <code>intern(name)</code> if <code>name</code> begins
+     * with <code>"["</code> and calling <code>intern("L" + name + ";")</code>
+     * in all other cases.
+     * 
+     * @param name non-null; the name of the class whose type is desired
+     * @return non-null; the corresponding type
+     * @throws IllegalArgumentException thrown if the name has
+     * invalid syntax
+     */
+    public static Type internClassName(String name) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (name.startsWith("[")) {
+            return intern(name);
+        }
+
+        return intern('L' + name + ';');
+    }
+
+    /**
+     * Constructs an instance corresponding to an "uninitialized type."
+     * This is a private constructor; use one of the public static
+     * methods to get instances.
+     * 
+     * @param descriptor non-null; the field descriptor for the type
+     * @param basicType basic type corresponding to this type; one of the
+     * <code>BT_*</code> constants
+     * @param newAt &gt;= -1 allocation bytecode index
+     */
+    private Type(String descriptor, int basicType, int newAt) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if ((basicType < 0) || (basicType >= BT_COUNT)) {
+            throw new IllegalArgumentException("bad basicType");
+        }
+
+        if (newAt < -1) {
+            throw new IllegalArgumentException("newAt < -1");
+        }
+
+        this.descriptor = descriptor;
+        this.basicType = basicType;
+        this.newAt = newAt;
+        this.arrayType = null;
+        this.componentType = null;
+        this.initializedType = null;
+    }
+
+    /**
+     * Constructs an instance corresponding to an "initialized type."
+     * This is a private constructor; use one of the public static
+     * methods to get instances.
+     * 
+     * @param descriptor non-null; the field descriptor for the type
+     * @param basicType basic type corresponding to this type; one of the
+     * <code>BT_*</code> constants
+     */
+    private Type(String descriptor, int basicType) {
+        this(descriptor, basicType, -1);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            /*
+             * Since externally-visible types are interned, this check
+             * helps weed out some easy cases.
+             */
+            return true;
+        }
+
+        if (!(other instanceof Type)) {
+            return false;
+        }
+
+        return descriptor.equals(((Type) other).descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Type other) {
+        return descriptor.compareTo(other.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return descriptor;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        switch (basicType) {
+            case BT_VOID:    return "void";
+            case BT_BOOLEAN: return "boolean";
+            case BT_BYTE:    return "byte";
+            case BT_CHAR:    return "char";
+            case BT_DOUBLE:  return "double";
+            case BT_FLOAT:   return "float";
+            case BT_INT:     return "int";
+            case BT_LONG:    return "long";
+            case BT_SHORT:   return "short";
+            case BT_OBJECT:  break;
+            default:         return descriptor;
+        }
+
+        if (isArray()) {
+            return getComponentType().toHuman() + "[]";
+        }
+
+        // Remove the "L...;" around the type and convert "/" to ".".
+        return getClassName().replace("/", ".");
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public Type getFrameType() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return INT;
+            }
+        }
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicType() {
+        return basicType;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicFrameType() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return BT_INT;
+            }
+        }
+
+        return basicType;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isConstant() {
+        return false;
+    }
+
+    /**
+     * Gets the descriptor.
+     * 
+     * @return non-null; the descriptor
+     */
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the name of the class this type corresponds to, in internal
+     * form. This method is only valid if this instance is for a
+     * normal reference type (that is, a reference type and
+     * additionally not a return address).
+     * 
+     * @return non-null; the internal-form class name
+     */
+    public String getClassName() {
+        if (className == null) {
+            if (!isReference()) {
+                throw new IllegalArgumentException("not an object type: " +
+                                                   descriptor);
+            }
+
+            if (descriptor.charAt(0) == '[') {
+                className = descriptor;
+            } else {
+                className = descriptor.substring(1, descriptor.length() - 1);
+            }
+        }
+
+        return className;
+    }
+
+    /**
+     * Gets the category. Most instances are category 1. <code>long</code>
+     * and <code>double</code> are the only category 2 types.
+     * 
+     * @see #isCategory1
+     * @see #isCategory2
+     * @return the category
+     */
+    public int getCategory() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return 2;
+            }
+        }
+
+        return 1;
+    }
+
+    /**
+     * Returns whether or not this is a category 1 type.
+     * 
+     * @see #getCategory
+     * @see #isCategory2
+     * @return whether or not this is a category 1 type
+     */
+    public boolean isCategory1() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether or not this is a category 2 type.
+     * 
+     * @see #getCategory
+     * @see #isCategory1
+     * @return whether or not this is a category 2 type
+     */
+    public boolean isCategory2() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is "intlike." An intlike type is one which, when
+     * placed on a stack or in a local, is automatically converted to an
+     * <code>int</code>.
+     * 
+     * @return whether this type is "intlike"
+     */
+    public boolean isIntlike() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is a primitive type. All types are either
+     * primitive or reference types.
+     * 
+     * @return whether this type is primitive
+     */
+    public boolean isPrimitive() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_DOUBLE:
+            case BT_FLOAT:
+            case BT_INT:
+            case BT_LONG:
+            case BT_SHORT:
+            case BT_VOID: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is a normal reference type. A normal
+     * reference type is a reference type that is not a return
+     * address. This method is just convenient shorthand for
+     * <code>getBasicType() == Type.BT_OBJECT</code>.
+     * 
+     * @return whether this type is a normal reference type
+     */
+    public boolean isReference() {
+        return (basicType == BT_OBJECT);
+    }
+
+    /**
+     * Gets whether this type is an array type. If this method returns
+     * <code>true</code>, then it is safe to use {@link #getComponentType}
+     * to determine the component type.
+     * 
+     * @return whether this type is an array type
+     */
+    public boolean isArray() {
+        return (descriptor.charAt(0) == '[');
+    }
+
+    /**
+     * Gets whether this type is an array type or is a known-null, and
+     * hence is compatible with array types.
+     * 
+     * @return whether this type is an array type
+     */
+    public boolean isArrayOrKnownNull() {
+        return isArray() || equals(KNOWN_NULL);
+    }
+
+    /**
+     * Gets whether this type represents an uninitialized instance. An
+     * uninitialized instance is what one gets back from the <code>new</code>
+     * opcode, and remains uninitialized until a valid constructor is
+     * invoked on it.
+     * 
+     * @return whether this type is "uninitialized"
+     */
+    public boolean isUninitialized() {
+        return (newAt >= 0);
+    }
+
+    /**
+     * Gets the bytecode index at which this uninitialized type was
+     * allocated.  This returns <code>Integer.MAX_VALUE</code> if this
+     * type is an uninitialized incoming parameter (i.e., the
+     * <code>this</code> of an <code>&lt;init&gt;</code> method) or
+     * <code>-1</code> if this type is in fact <i>initialized</i>.
+     * 
+     * @return &gt;= -1; the allocation bytecode index
+     */
+    public int getNewAt() {
+        return newAt;
+    }
+
+    /**
+     * Gets the initialized type corresponding to this instance, but only
+     * if this instance is in fact an uninitialized object type.
+     * 
+     * @return non-null; the initialized type
+     */
+    public Type getInitializedType() {
+        if (initializedType == null) {
+            throw new IllegalArgumentException("initialized type: " +
+                                               descriptor);
+        }
+
+        return initializedType;
+    }
+
+    /**
+     * Gets the type corresponding to an array of this type.
+     * 
+     * @return non-null; the array type
+     */
+    public Type getArrayType() {
+        if (arrayType == null) {
+            arrayType = putIntern(new Type('[' + descriptor, BT_OBJECT));
+        }
+
+        return arrayType;
+    }
+
+    /**
+     * Gets the component type of this type. This method is only valid on
+     * array types.
+     * 
+     * @return non-null; the component type
+     */
+    public Type getComponentType() {
+        if (componentType == null) {
+            if (descriptor.charAt(0) != '[') {
+                throw new IllegalArgumentException("not an array type: " +
+                                                   descriptor);
+            }
+            componentType = intern(descriptor.substring(1));
+        }
+
+        return componentType;
+    }
+
+    /**
+     * Returns a new interned instance which is identical to this one, except
+     * it is indicated as uninitialized and allocated at the given bytecode
+     * index. This instance must be an initialized object type.
+     * 
+     * @param newAt &gt;= 0; the allocation bytecode index
+     * @return non-null; an appropriately-constructed instance
+     */
+    public Type asUninitialized(int newAt) {
+        if (newAt < 0) {
+            throw new IllegalArgumentException("newAt < 0");
+        }
+
+        if (!isReference()) {
+            throw new IllegalArgumentException("not a reference type: " +
+                                               descriptor);
+        }
+
+        if (isUninitialized()) {
+            /*
+             * Dealing with uninitialized types as a starting point is
+             * a pain, and it's not clear that it'd ever be used, so
+             * just disallow it.
+             */
+            throw new IllegalArgumentException("already uninitialized: " +
+                                               descriptor);
+        }
+
+        /*
+         * Create a new descriptor that is unique and shouldn't conflict
+         * with "normal" type descriptors
+         */
+        String newDesc = 'N' + Hex.u2(newAt) + descriptor;
+        Type result = new Type(newDesc, BT_OBJECT, newAt);
+        result.initializedType = this;
+        return putIntern(result);
+    }
+
+    /**
+     * Puts the given instance in the intern table if it's not already
+     * there. If a conflicting value is already in the table, then leave it.
+     * Return the interned value.
+     * 
+     * @param type non-null; instance to make interned
+     * @return non-null; the actual interned object
+     */
+    private static Type putIntern(Type type) {
+        synchronized (internTable) {
+            String descriptor = type.getDescriptor();
+            Type already = internTable.get(descriptor);
+            if (already != null) {
+                return already;
+            }
+            internTable.put(descriptor, type);
+            return type;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeBearer.java b/dx/src/com/android/dx/rop/type/TypeBearer.java
new file mode 100644
index 0000000..b9e4ea5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeBearer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Object which has an associated type, possibly itself.
+ */
+public interface TypeBearer
+        extends ToHuman {
+    /**
+     * Gets the type associated with this instance.
+     *
+     * @return non-null; the type
+     */
+    public Type getType();
+
+    /**
+     * Gets the frame type corresponding to this type. This method returns
+     * <code>this</code>, except if {@link Type#isIntlike} on the underlying
+     * type returns <code>true</code> but the underlying type is not in
+     * fact {@link Type#INT}, in which case this method returns an instance
+     * whose underlying type <i>is</i> <code>INT</code>.
+     *
+     * @return non-null; the frame type for this instance
+     */
+    public TypeBearer getFrameType();
+
+    /**
+     * Gets the basic type corresponding to this instance.
+     *
+     * @return the basic type; one of the <code>BT_*</code> constants
+     * defined by {@link Type}
+     */
+    public int getBasicType();
+
+    /**
+     * Gets the basic type corresponding to this instance's frame type. This
+     * is equivalent to <code>getFrameType().getBasicType()</code>, and
+     * is the same as calling <code>getFrameType()</code> unless this
+     * instance is an int-like type, in which case this method returns
+     * <code>BT_INT</code>.
+     * 
+     * @see #getBasicType
+     * @see #getFrameType
+     * 
+     * @return the basic frame type; one of the <code>BT_*</code> constants
+     * defined by {@link Type}
+     */
+    public int getBasicFrameType();
+
+    /**
+     * Returns whether this instance represents a constant value.
+     * 
+     * @return <code>true</code> if this instance represents a constant value
+     * and <code>false</code> if not
+     */
+    public boolean isConstant();
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeList.java b/dx/src/com/android/dx/rop/type/TypeList.java
new file mode 100644
index 0000000..0944fe2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeList.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+/**
+ * List of {@link Type} instances (or of things that contain types).
+ */
+public interface TypeList {
+    /**
+     * Returns whether this instance is mutable. Note that the
+     * <code>TypeList</code> interface itself doesn't provide any
+     * means of mutation, but that doesn't mean that there isn't an
+     * extra-interface way of mutating an instance.
+     * 
+     * @return <code>true</code> if this instance is mutable or
+     * <code>false</code> if it is immutable
+     */
+    public boolean isMutable();
+    
+    /**
+     * Gets the size of this list.
+     *
+     * @return &gt;= 0; the size
+     */
+    public int size();
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw <code>NullPointerException</code>.
+     *
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return non-null; the indicated element
+     */
+    public Type getType(int n);
+
+    /**
+     * Gets the number of 32-bit words required to hold instances of
+     * all the elements of this list. This is a sum of the widths (categories)
+     * of all the elements.
+     * 
+     * @return &gt;= 0; the required number of words
+     */
+    public int getWordCount();
+
+    /**
+     * Returns a new instance which is identical to this one, except that
+     * the given item is appended to the end and it is guaranteed to be
+     * immutable.
+     * 
+     * @param type non-null; item to append
+     * @return non-null; an appropriately-constructed instance
+     */
+    public TypeList withAddedType(Type type);
+}
diff --git a/dx/src/com/android/dx/rop/type/package.html b/dx/src/com/android/dx/rop/type/package.html
new file mode 100644
index 0000000..93d9d5f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Implementation of classes that represent types (classes or primitives).</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
new file mode 100644
index 0000000..86fcf81
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.IntList;
+
+/**
+ * This class maps one register space into another, with
+ * each mapping built up individually and added via addMapping()
+ */
+public class BasicRegisterMapper
+        extends RegisterMapper {
+
+    /** indexed by old register, containing new name */
+    private IntList oldToNew;
+
+    /** Running count of used registers in new namespace */
+    private int runningCountNewRegisters;
+
+    /**
+     * Creates a new OneToOneRegisterMapper
+     * @param countOldRegisters the number of registers in the old name space
+     */
+    public BasicRegisterMapper(int countOldRegisters) {
+        oldToNew = new IntList(countOldRegisters);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getNewRegisterCount() {
+        return runningCountNewRegisters;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterSpec map(RegisterSpec registerSpec) {
+        if (registerSpec == null) {
+            return null;
+        }
+
+        int newReg;
+        try {
+            newReg = oldToNew.get(registerSpec.getReg());
+        } catch (IndexOutOfBoundsException ex) {
+            newReg = -1;
+        }
+
+        if (newReg < 0) {
+            throw new RuntimeException("no mapping specified for register");
+        }
+
+        return registerSpec.withReg(newReg);
+    }
+
+    /**
+     * Returns the new-namespace mapping for the specified
+     * old-namespace register, or -1 if one exists
+     *
+     * @param oldReg &gt;=0; old-namespace register
+     * @return new-namespace register or -1 if none.
+     */
+    public int oldToNew(int oldReg) {
+        if(oldReg >= oldToNew.size()) {
+            return -1;
+        }
+        return oldToNew.get(oldReg);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("Old\tNew\n");
+        int sz = oldToNew.size();
+        for(int i = 0; i < sz; i++) {
+            sb.append(i);
+            sb.append('\t');
+            sb.append(oldToNew.get(i));
+            sb.append('\n');
+        }
+
+        sb.append("new reg count:");
+
+        sb.append(runningCountNewRegisters);
+        sb.append('\n');
+
+        return sb.toString();
+    }
+
+    /**
+     * adds a mapping to the mapper. If oldReg has already been mapped,
+     * overwrites previous mapping with new mapping.
+     *
+     * @param oldReg >=0
+     * @param newReg >=0
+     * @param category width of reg (1 or 2)
+     */
+    public void addMapping(int oldReg, int newReg, int category) {
+        if (oldReg >= oldToNew.size()) {
+            // expand the array as necessary
+            for (int i = oldReg - oldToNew.size(); i >= 0; i--) {
+                oldToNew.add(-1);
+            }
+        }
+        oldToNew.set(oldReg, newReg);
+
+        if (runningCountNewRegisters < (newReg + category)) {
+            runningCountNewRegisters = newReg + category;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/ConstCollector.java b/dx/src/com/android/dx/ssa/ConstCollector.java
new file mode 100644
index 0000000..afdede7
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/ConstCollector.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.cst.CstString;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+
+/**
+ * Collects constants that are used more than once at the top of the
+ * method block. This increases register usage by about 5% but decreases
+ * insn size by about 3%.
+ */
+public class ConstCollector {
+
+    /** Maximum constants to collect per method. Puts cap on reg use */
+    private static final int MAX_COLLECTED_CONSTANTS = 5;
+
+    /**
+     * Also collect string consts, although they can throw exceptions.
+     * This is off now because it just doesn't seem to gain a whole lot.
+     * TODO if you turn this on, you must change SsaInsn.hasSideEffect()
+     * to return false for const-string insns whose exceptions are not
+     * caught in the current method.
+     */
+    private static boolean COLLECT_STRINGS = false;
+
+    /**
+     * If true, allow one local var to be involved with a collected const.
+     * Turned off because it mostly just inserts more moves.
+     */
+    private static boolean COLLECT_ONE_LOCAL = false;
+
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Process a method.
+     *
+     * @param ssaMethod non-null; method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        ConstCollector dc;
+
+        dc = new ConstCollector(ssaMethod);
+            
+        dc.run();
+    }
+
+    private ConstCollector(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+    }
+
+    /**
+     * Applies the optimization.
+     */
+    private void run() {
+        int regSz = ssaMeth.getRegCount();
+
+        ArrayList<TypedConstant> constantList
+                = getConstsSortedByCountUse();
+    
+        int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS);
+
+        SsaBasicBlock start = ssaMeth.getEntryBlock();
+
+        // Constant to new register containing the constant
+        HashMap<TypedConstant, RegisterSpec> newRegs
+                = new HashMap<TypedConstant, RegisterSpec> (toCollect);
+
+        for (int i = 0; i < toCollect; i++) {
+            TypedConstant cst = constantList.get(i);
+            RegisterSpec result
+                    = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst);
+
+            Rop constRop = Rops.opConst(cst);
+
+            if (constRop.getBranchingness() == Rop.BRANCH_NONE) {
+                start.addInsnToHead(
+                        new PlainCstInsn(Rops.opConst(cst),
+                                SourcePosition.NO_INFO, result,
+                                RegisterSpecList.EMPTY, cst));
+            } else {
+                // We need two new basic blocks along with the new insn
+                SsaBasicBlock entryBlock = ssaMeth.getEntryBlock();
+                SsaBasicBlock successorBlock
+                        = entryBlock.getPrimarySuccessor();
+
+                /*
+                 * Insert a block containing the const insn
+                 */
+                SsaBasicBlock constBlock
+                        = entryBlock.insertNewSuccessor(successorBlock);
+
+                constBlock.replaceLastInsn(
+                        new ThrowingCstInsn(constRop, SourcePosition.NO_INFO,
+                                RegisterSpecList.EMPTY,
+                                StdTypeList.EMPTY, cst));
+
+                /*
+                 * Insert a block containing the move-result-pseudo insn
+                 */
+
+                SsaBasicBlock resultBlock
+                        = constBlock.insertNewSuccessor(successorBlock);
+
+                resultBlock.addInsnToHead(
+                        new PlainInsn(
+                                Rops.opMoveResultPseudo(result.getTypeBearer()),
+                                SourcePosition.NO_INFO,
+                                result, RegisterSpecList.EMPTY));
+            }
+
+            newRegs.put(cst, result);
+        }
+
+        updateConstUses(newRegs, regSz);
+    }
+
+    /**
+     * Gets all of the collectable constant values used in this method,
+     * sorted by most used first. Skips non-collectable consts, such as
+     * non-string object constants
+     *
+     * @return non-null; list of constants in most-to-least used order
+     */
+    private ArrayList<TypedConstant> getConstsSortedByCountUse() {
+        int regSz = ssaMeth.getRegCount();
+
+        final HashMap<TypedConstant, Integer> countUses
+                = new HashMap<TypedConstant, Integer>();
+
+        // Each collected constant can be used by just one local
+        // (used only if COLLECT_ONE_LOCAL is true)
+        final HashSet<TypedConstant> usedByLocal
+                = new HashSet<TypedConstant>();
+
+        // Count how many times each const value is used
+        for (int i = 0; i < regSz; i++) {
+            SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+            if (insn == null) continue;
+
+            RegisterSpec result = insn.getResult();
+
+            TypeBearer typeBearer = insn.getResult().getTypeBearer();
+
+            if (!typeBearer.isConstant()) continue;
+
+            TypedConstant cst = (TypedConstant) typeBearer;
+
+            if (insn.canThrow()) {
+                /*
+                 * Don't move anything other than strings -- the risk
+                 * of changing where an exception is thrown is too high.                
+                 */
+                if (!(cst instanceof CstString) || !COLLECT_STRINGS) {
+                    continue;
+                }
+                /*
+                 * We can't move any throwable const whose throw will be caught,
+                 * so don't count them.                 
+                 */
+                if (insn.getBlock().getSuccessors().cardinality() > 1) {
+                    continue;
+                }
+            }
+
+            // TODO might be nice to try and figure out which local wins most
+            // when collected
+            if (ssaMeth.isRegALocal(result)) {
+                if (!COLLECT_ONE_LOCAL) {
+                    continue;
+                } else {
+                    if (usedByLocal.contains(cst)) {
+                        // Count one local usage only
+                        continue;
+                    } else {
+                        usedByLocal.add(cst);
+                    }
+                }
+            }
+
+            Integer has = countUses.get(cst);
+            if (has == null) {
+                countUses.put(cst, 1);
+            } else {
+                countUses.put(cst, has + 1);
+            }
+        }
+
+        // Collect constants that have been reused
+        Iterator<TypedConstant> it = countUses.keySet().iterator();
+        ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>();
+        while (it.hasNext()) {
+            TypedConstant cst = it.next();
+
+            if (countUses.get(cst) > 1) {
+                constantList.add(cst);
+            }
+        }
+
+        // Sort by use, with most used at the beginning of the list
+        Collections.sort(constantList, new Comparator<Constant>() {
+            public int compare(Constant a, Constant b) {
+                int ret;
+                ret = countUses.get(b) - countUses.get(a);
+
+                if (ret == 0) {
+                    /*
+                     * Provide sorting determinisim for constants with same
+                     * usage count.
+                     */
+                    ret = a.compareTo(b);
+                }
+
+                return ret;
+            }
+            public boolean equals (Object obj) {
+                return obj == this;
+            }
+        });
+        return constantList;
+    }
+
+    /**
+     * Inserts mark-locals if necessary when changing a register. If
+     * the definition of <code>origReg</code> is associated with a local
+     * variable, then insert a mark-local for <code>newReg</code> just below
+     * it. We expect the definition of  <code>origReg</code> to ultimately
+     * be removed by the dead code eliminator
+     * 
+     * @param origReg non-null; original register
+     * @param newReg non-null; new register that will replace
+     * <code>origReg</code>
+     */
+    private void fixLocalAssignment(RegisterSpec origReg, RegisterSpec newReg) {
+        for (SsaInsn use: ssaMeth.getUseListForRegister(origReg.getReg())) {
+            RegisterSpec localAssignment = use.getLocalAssignment();
+            if (localAssignment == null) {
+                continue;
+            }
+
+            if (use.getResult() == null) {
+                // this is a mark-local. it will be updated when all uses
+                // are updated
+                continue;
+            }
+
+            LocalItem local = localAssignment.getLocalItem();
+
+            /*
+             * un-associate original use
+             */
+            use.setResultLocal(null);
+
+            /*
+             * now add a mark-local to the new reg immediately after
+             */                       
+            newReg = newReg.withLocalItem(local);
+
+            SsaInsn newInsn
+                    = SsaInsn.makeFromRop(
+                        new PlainInsn(Rops.opMarkLocal(newReg),
+                        SourcePosition.NO_INFO, null,
+                                RegisterSpecList.make(newReg)),
+                    use.getBlock());
+
+            ArrayList<SsaInsn> insns = use.getBlock().getInsns();
+
+            insns.add(insns.indexOf(use) + 1, newInsn);
+        }
+    }
+
+    /**
+     * Updates all uses of various consts to use the values in the newly
+     * assigned registers.
+     *
+     * @param newRegs non-null; mapping between constant and new reg
+     * @param origRegCount &gt;=0; original SSA reg count, not including
+     * newly added constant regs
+     */
+    private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs,
+            int origRegCount) {
+
+        // Set of constants associated with a local variable
+        // Used only if COLLECT_ONE_LOCAL is true
+        final HashSet<TypedConstant> usedByLocal
+                = new HashSet<TypedConstant>();
+
+        final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+
+        for (int i = 0; i < origRegCount; i++) {
+            SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+            if (insn == null) {
+                continue;
+            }
+
+            final RegisterSpec origReg = insn.getResult();
+            TypeBearer typeBearer = insn.getResult().getTypeBearer();
+
+            if (!typeBearer.isConstant()) continue;
+
+            TypedConstant cst = (TypedConstant) typeBearer;
+            final RegisterSpec newReg = newRegs.get(cst);
+
+            if (newReg == null) {
+                continue;
+            }
+
+            if (ssaMeth.isRegALocal(origReg)) {
+                if (!COLLECT_ONE_LOCAL) {
+                    continue;                    
+                } else {
+                    // TODO if the same local gets the same cst multiple times,
+                    // it would be nice to reuse the register
+                    if (usedByLocal.contains(cst)) {
+                        continue;
+                    } else {
+                        usedByLocal.add(cst);
+                        fixLocalAssignment(origReg, newRegs.get(cst));
+                    }
+                }
+            }
+
+            // Maps an original const register to the new collected register
+            RegisterMapper mapper = new RegisterMapper() {
+                @Override
+                public int getNewRegisterCount() {
+                    return ssaMeth.getRegCount();
+                }
+
+                @Override
+                public RegisterSpec map(RegisterSpec registerSpec) {
+                    if (registerSpec.getReg() == origReg.getReg()) {
+                        return newReg.withLocalItem(registerSpec.getLocalItem());
+                    }
+
+                    return registerSpec;
+                }
+            };
+
+            for (SsaInsn use: useList[origReg.getReg()]) {
+                if (use.canThrow()
+                        && use.getBlock().getSuccessors().cardinality() > 1) {
+                    continue;
+                }
+                use.mapSourceRegisters(mapper);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
new file mode 100644
index 0000000..4fded44
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.Insn;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form".
+ *
+ * TODO this algorithm is more efficient if run in reverse from exit
+ * block to entry block.
+ */
+public class DeadCodeRemover {
+
+    /** method we're processing */
+    private SsaMethod ssaMeth;
+    /** ssaMeth.getRegCount() */
+    private int regCount;
+
+    /**
+     * indexed by register: whether reg should be examined
+     * (does it correspond to a no-side-effect insn?)
+     */
+    private BitSet worklist;
+
+    /** use list indexed by register; modified during operation */
+    private ArrayList<SsaInsn>[] useList;
+
+    /**
+     * Process a method with the dead-code remver
+     * @param ssaMethod method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        DeadCodeRemover dc;
+
+        dc = new DeadCodeRemover(ssaMethod);
+            
+        dc.run();
+    }
+
+    private DeadCodeRemover(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+
+        regCount = ssaMethod.getRegCount();
+
+        worklist = new BitSet(regCount);
+
+        useList = ssaMeth.getUseListCopy();
+    }
+
+    /**
+     * Run the dead code remover
+     */
+    private void run() {
+
+        HashSet<SsaInsn> deletedInsns = (HashSet<SsaInsn>) new HashSet();
+
+        ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist));
+
+        int regV;
+
+        while ( 0 <=  (regV = worklist.nextSetBit(0)) ) {
+            worklist.clear(regV);
+
+            if (useList[regV].size() == 0
+                    || isCircularNoSideEffect(regV, null)) {
+
+                SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV);
+
+                // This insn has already been deleted
+                if (deletedInsns.contains(insnS)) {
+                    continue;
+                }
+
+                RegisterSpecList sources = insnS.getSources();
+
+                int sz = sources.size();
+                for (int i = 0; i < sz; i++) {
+
+                    // Delete this insn from all usage lists
+                    RegisterSpec source = sources.get(i);
+                    useList[source.getReg()].remove(insnS);
+
+                    if (!hasSideEffect(
+                            ssaMeth.getDefinitionForRegister(
+                                    source.getReg()))) {
+                        /*
+                         * Only registers who's definition has no side effect
+                         * should be added back to the worklist
+                         */
+                        worklist.set(source.getReg());
+                    }
+                }
+
+                // Schedule this insn for later deletion
+                deletedInsns.add(insnS);
+            }
+        }
+
+        ssaMeth.deleteInsns(deletedInsns);
+    }
+
+    /**
+     * Returns true if the only uses of this register form a circle of
+     * operations with no side effects
+     * @param regV register to examine
+     * @param set a set of registers that we've already determined
+     * are only used as sources in operations with no side effect or null
+     * if this is the first recursion
+     * @return true if usage is circular without side effect
+     */
+    private boolean isCircularNoSideEffect(int regV, BitSet set) {
+        if ((set != null) && set.get(regV)) {
+            return true;
+        }
+
+        for (SsaInsn use: useList[regV]) {
+            if (hasSideEffect(use)) {
+                return false;
+            }
+        }
+
+        if (set == null) {
+            set = new BitSet(regCount);
+        }
+
+        // This register is only used in operations that have no side effect.
+        set.set(regV);
+
+        for (SsaInsn use: useList[regV]) {
+            RegisterSpec result = use.getResult();
+
+            if (result == null
+                    || !isCircularNoSideEffect(result.getReg(), set)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if this insn has a side-effect. Returns true
+     * if the insn is null for reasons stated in the code block.
+     * @param insn null-ok; instruction in question
+     * @return true if it has a side-effect
+     */
+    private static boolean hasSideEffect(SsaInsn insn) {
+        if (insn == null) {
+            /* while false would seem to make more sense here, true
+             * prevents us from adding this back to a worklist unnecessarally
+             */
+            return true;
+        }
+
+        return insn.hasSideEffect();        
+    }
+
+    /**
+     * A callback class used to build up the initial worklist of
+     * registers defined by an instruction with no side effect.
+     */
+    static class NoSideEffectVisitor implements SsaInsn.Visitor {
+        BitSet noSideEffectRegs;
+
+        /**
+         * Passes in data structures that will be filled out after
+         * ssaMeth.forEachInsn() is called with this instance.
+         *
+         * @param noSideEffectRegs to-build bitset of regs that are
+         * results of regs with no side effects
+         */
+        NoSideEffectVisitor(BitSet noSideEffectRegs) {
+            this.noSideEffectRegs = noSideEffectRegs;
+        }
+
+        /** {@inheritDoc} */
+        public void visitMoveInsn (NormalSsaInsn insn) {
+            // If we're tracking local vars, some moves have side effects
+            if (!hasSideEffect(insn)) {
+                noSideEffectRegs.set(insn.getResult().getReg());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitPhiInsn (PhiInsn phi) {
+            // If we're tracking local vars, then some phis have side effects
+            if (!hasSideEffect(phi)) {
+                noSideEffectRegs.set(phi.getResult().getReg());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitNonMoveInsn (NormalSsaInsn insn) {
+            RegisterSpec result = insn.getResult();
+            if (!hasSideEffect(insn) && result != null) {
+                noSideEffectRegs.set(result.getReg());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/DomFront.java b/dx/src/com/android/dx/ssa/DomFront.java
new file mode 100644
index 0000000..ea089ec
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DomFront.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Calculates the dominance-frontiers of a methot's basic blocks.
+ * Algorithm from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+public class DomFront {
+    private static boolean DEBUG = false;
+
+    private final SsaMethod meth;
+    private final ArrayList<SsaBasicBlock> nodes;
+    private final DomInfo[] domInfos;
+
+    /**
+     * Dominance-frontier information for a single basic block.
+     */
+    public static class DomInfo {
+        /** non-null; the dominance frontier set indexed by block index */
+        IntSet dominanceFrontiers;
+        /** &gt= 0 after run(); the index of the immediate dominator */
+        int idom = -1;
+        /** depth-first traversal index */
+        int traversalIndex;
+    }
+
+    /**
+     * Constructs instance. Call {@link DomFront#run} to process. 
+     * @param meth
+     */
+    public DomFront(SsaMethod meth) {
+        this.meth = meth;
+        nodes = meth.getBlocks();
+
+        int szNodes = nodes.size();
+        domInfos = new DomInfo[szNodes];
+
+        for (int i = 0; i < szNodes; i++) {
+            domInfos[i] = new DomInfo();
+        }
+    }
+
+    /**
+     * Calculates the dominance frontier information for the method.
+     *
+     * @return non-null; an array of DomInfo structures
+     */
+    public DomInfo[] run() {
+        int szNodes = nodes.size();
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                SsaBasicBlock node = nodes.get(i);
+                System.out.println("pred[" + i + "]: "
+                        + node.getPredecessors());
+            }
+        }
+
+        Dominators methDom = new Dominators(domInfos, false);
+        methDom.run(meth);
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                DomInfo info = domInfos[i];
+                System.out.println("idom[" + i + "]: "
+                        + info.idom);
+            }
+        }
+
+        buildDomTree();
+        
+        if (DEBUG) {
+            debugPrintDomChildren();
+        }
+
+        for (int i = 0; i < szNodes; i++) {
+            domInfos[i].dominanceFrontiers
+                    = SetFactory.makeDomFrontSet(szNodes);
+        }
+
+        calcDomFronts();
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                System.out.println("df[" + i + "]: "
+                        + domInfos[i].dominanceFrontiers);
+            }
+        }
+
+        return domInfos;
+    }
+
+    private void debugPrintDomChildren() {
+        int szNodes = nodes.size();
+
+        for (int i = 0; i < szNodes; i++) {
+            SsaBasicBlock node = nodes.get(i);
+            StringBuffer sb = new StringBuffer();
+
+            sb.append('{');
+            boolean comma = false;
+            for (SsaBasicBlock child: node.getDomChildren()) {
+                if (comma) {
+                    sb.append(',');
+                }
+                sb.append(child);
+                comma = true;
+            }
+            sb.append('}');
+
+            System.out.println("domChildren[" + node + "]: "
+                    + sb);
+        }
+    }
+
+    /**
+     * The dominators algorithm leaves us knowing who the immediate dominator
+     * is for each node. This sweeps the node list and builds the proper
+     * dominance tree.
+     */
+    private void buildDomTree() {
+        int szNodes = nodes.size();
+
+        for (int i = 0; i < szNodes; i++) {
+            DomInfo info = domInfos[i];
+            SsaBasicBlock domParent = nodes.get(info.idom);
+            domParent.addDomChild(nodes.get(i));
+        }
+    }
+
+    /**
+     * Calculates the dominance-frontier set.
+     * from "A Simple, Fast Dominance Algorithm" by Cooper,
+     * Harvey, and Kennedy; transliterated to Java.
+     */
+    private void calcDomFronts() {
+        int szNodes = nodes.size();
+
+        for (int b = 0; b < szNodes; b++) {
+            SsaBasicBlock nb = nodes.get(b);
+            DomInfo nbInfo = domInfos[b];
+            BitSet pred = nb.getPredecessors();
+            if (pred.cardinality() > 1) {
+                for (int i = pred.nextSetBit(0); i >= 0;
+                     i = pred.nextSetBit(i + 1)) {
+
+                    for(int runnerIndex = i
+                            ; runnerIndex != nbInfo.idom
+                            ;) {
+                        // We can stop if we hit a block we already
+                        // added label to, since we must be at a part
+                        // of the dom tree we have seen before.
+                        DomInfo runnerInfo = domInfos[runnerIndex];
+                        if (runnerInfo.dominanceFrontiers.has(b))
+                            break;
+                        // "add b to runner's dominance frontier set"
+                        runnerInfo.dominanceFrontiers.add(b);
+                        runnerIndex = runnerInfo.idom;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/Dominators.java b/dx/src/com/android/dx/ssa/Dominators.java
new file mode 100644
index 0000000..1af2cbc
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Dominators.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * This class computes dominator and post-dominator information using the
+ * Lengauer-Tarjan method.
+ *
+ * See A Fast Algorithm for Finding Dominators in a Flowgraph
+ * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
+ *
+ * This implementation runs in time O(n log n).  The time bound
+ * could be changed to O(n * ack(n)) with a small change to the link and eval,
+ * and an addition of a child field to the DFS info. In reality, the constant
+ * overheads are high enough that the current method is faster in all but the
+ * strangest artificially constructed examples.
+ *
+ * The basic idea behind this algorithm is to perform a DFS walk, keeping track
+ * of various info about parents.  We then use this info to calculate the
+ * dominators, using union-find structures to link together the DFS info,
+ * then finally evaluate the union-find results to get the dominators.
+ * This implementation is m log n because it does not perform union by
+ * rank to keep the union-find tree balanced.
+ */
+public final class Dominators {
+    /* postdom is true if we want post dominators. */
+    private boolean postdom;
+    /* Method's basic blocks. */
+    private ArrayList<SsaBasicBlock> blocks;
+
+    private static final class DFSInfo {
+        int semidom;
+        SsaBasicBlock parent;
+        // rep(resentative) is known as "label" in the paper. It is the node
+        // that our block's DFS info has been unioned to.
+        SsaBasicBlock rep;
+        SsaBasicBlock ancestor;
+        ArrayList<SsaBasicBlock> bucket;
+
+        public DFSInfo() {
+            bucket = new ArrayList<SsaBasicBlock>();
+        }
+
+    }
+
+    /** Indexed by basic block index */
+    private DFSInfo[] info;
+    private ArrayList<SsaBasicBlock> vertex;
+
+    private DomFront.DomInfo domInfos[];
+
+    private BitSet getSuccs(SsaBasicBlock block) {
+        if (postdom) {
+            return block.getPredecessors();
+        } else {
+            return block.getSuccessors();
+        }
+    }
+
+    private BitSet getPreds(SsaBasicBlock block) {
+        if (postdom) {
+            return block.getSuccessors();
+        } else {
+            return block.getPredecessors();
+        }
+    }
+
+    /**
+     * Performs path compress on the DFS info.
+     * @param in Basic block whose DFS info we are path compressing.
+     */
+    private void compress(SsaBasicBlock in) {
+        DFSInfo bbInfo = info[in.getIndex()];
+        DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];
+
+        if (ancestorbbInfo.ancestor != null) {
+            ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
+            HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
+            worklist.add(in);
+            
+            while (!worklist.isEmpty()) {
+                int wsize = worklist.size();
+                SsaBasicBlock v = worklist.get(wsize - 1);
+                DFSInfo vbbInfo = info[v.getIndex()];
+                SsaBasicBlock vAncestor = vbbInfo.ancestor;
+                DFSInfo vabbInfo = info[vAncestor.getIndex()];
+
+                // Make sure we process our ancestor before ourselves.
+                if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
+                    worklist.add(vAncestor);
+                    continue;
+                }
+                worklist.remove(wsize - 1);
+
+                // Update based on ancestor info
+                if (vabbInfo.ancestor == null) {
+                    continue;
+                }
+                SsaBasicBlock vAncestorRep = vabbInfo.rep;
+                SsaBasicBlock vRep = vbbInfo.rep;
+                if (info[vAncestorRep.getIndex()].semidom
+                        < info[vRep.getIndex()].semidom) {
+                    vbbInfo.rep = vAncestorRep;
+                }
+                vbbInfo.ancestor = vabbInfo.ancestor;
+            }
+        }
+    }
+    private SsaBasicBlock eval(SsaBasicBlock v) {
+        DFSInfo bbInfo = info[v.getIndex()];
+        if (bbInfo.ancestor == null) {
+            return v;
+        }
+        compress(v);
+        return bbInfo.rep;
+    }
+
+    /**
+     * Callback for depth-first walk through control flow graph (either
+     * from the entry block or the exit block). Records the traversal order
+     * in the <code>info</code>list.
+     */
+    private class DfsWalker implements SsaBasicBlock.Visitor {
+        int dfsNum = 0;
+
+        public void visitBlock (SsaBasicBlock v, SsaBasicBlock parent) {
+            DFSInfo bbInfo = new DFSInfo();
+            bbInfo.semidom = ++dfsNum;
+            bbInfo.rep = v;
+            bbInfo.parent = parent;
+            vertex.add(v);
+            info[v.getIndex()] = bbInfo;
+        }
+    }
+
+    /**
+     * Performs dominator/post-dominator calculation for the control flow graph.
+     * @param meth Method to analyze
+     */
+    public void run(SsaMethod meth) {
+
+        this.blocks = meth.getBlocks();
+        this.info = new DFSInfo[blocks.size() + 2];
+        this.vertex = new ArrayList<SsaBasicBlock>();
+        SsaBasicBlock root = postdom
+                ? meth.getExitBlock() : meth.getEntryBlock();
+
+        if (root != null) {
+            vertex.add(root);
+            domInfos[root.getIndex()].idom = root.getIndex();
+        }
+        
+        // First we perform a DFS numbering of the blocks, by numbering the dfs
+        // tree roots
+
+        DfsWalker walker = new DfsWalker();
+        meth.forEachBlockDepthFirst(postdom, walker);
+
+        // the largest semidom number assigned
+        int dfsMax = vertex.size() - 1;
+
+        // Now calculate semidominators.
+        for (int i = dfsMax; i >= 2; --i) {
+            SsaBasicBlock w = vertex.get(i);
+            DFSInfo wInfo = info[w.getIndex()];
+
+            BitSet preds = getPreds(w);
+            for (int j = preds.nextSetBit(0);
+                    j >= 0;
+                    j = preds.nextSetBit(j + 1)) {
+                SsaBasicBlock predBlock = blocks.get(j);
+                DFSInfo predInfo = info[predBlock.getIndex()];
+                // PredInfo may not exist in case the predecessor is not
+                // reachable
+                if (predInfo != null) {
+                    int predSemidom = info[eval(predBlock).getIndex()].semidom;
+                    if (predSemidom < wInfo.semidom) {
+                        wInfo.semidom = predSemidom;
+                    }
+                }
+            }
+            info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);
+
+            // Normally we would call link here, but in our  m log n
+            // implementation this is equivalent to the following single line
+            wInfo.ancestor = wInfo.parent;
+
+            // Implicity define idom for each vertex
+            ArrayList<SsaBasicBlock> wParentBucket;
+            wParentBucket = info[wInfo.parent.getIndex()].bucket;
+
+            while (!wParentBucket.isEmpty()) {
+                int lastItem = wParentBucket.size() - 1;
+                SsaBasicBlock last = wParentBucket.remove(lastItem);
+                SsaBasicBlock U = eval(last);
+                if (info[U.getIndex()].semidom
+                        < info[last.getIndex()].semidom) {
+                    domInfos[last.getIndex()].idom = U.getIndex();
+                } else {
+                    domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
+                }
+            }
+        }
+        // Now explicitly define the immediate dominator of each vertex
+        for (int i =  2; i <= dfsMax; ++i) {
+            SsaBasicBlock w = vertex.get(i);
+            if (domInfos[w.getIndex()].idom
+                    != vertex.get(info[w.getIndex()].semidom).getIndex()) {
+                domInfos[w.getIndex()].idom
+                        = domInfos[domInfos[w.getIndex()].idom].idom;
+            }
+        }
+    }
+
+    /**
+     * @param postdom true for postdom information, false for normal dom info
+     */
+    public Dominators(DomFront.DomInfo[] domInfos, boolean postdom) {
+        this.domInfos = domInfos;
+        this.postdom = postdom;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
new file mode 100644
index 0000000..be678dd
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.ssa.back.InterferenceGraph;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A register mapper that keeps track of the accumulated interference
+ * information for the registers in the new namespace.
+ *
+ * Please note that this mapper requires that the old namespace does not
+ * have variable register widths/categories, and the new namespace does.
+ */
+public class InterferenceRegisterMapper extends BasicRegisterMapper {
+
+    /**
+     * Array of interference sets. ArrayList is indexed by new namespace
+     * and BitIntSet's are indexed by old namespace.  The list expands
+     * as needed and missing items are assumed to interfere with nothing.
+     *
+     * Bit sets are always used here, unlike elsewhere, because the max
+     * size of this matrix will be (countSsaRegs * countRopRegs), which may
+     * grow to hundreds of K but not megabytes.
+     */
+    private final ArrayList<BitIntSet> newRegInterference;
+
+    /**
+     * The interference graph for the old namespace
+     */
+    private final InterferenceGraph oldRegInterference;
+
+    /**
+     * @param countOldRegisters number of registers in old namespace.
+     */
+    public InterferenceRegisterMapper(
+            InterferenceGraph oldRegInterference,
+            int countOldRegisters) {
+        super(countOldRegisters);
+
+        newRegInterference = new ArrayList<BitIntSet>();
+        this.oldRegInterference = oldRegInterference;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addMapping(int oldReg, int newReg, int category) {
+        super.addMapping(oldReg, newReg, category);
+
+        addInterfence(newReg, oldReg);
+
+        if (category == 2) {
+            addInterfence(newReg + 1, oldReg);
+        }
+    }
+
+    /**
+     * Checks to see if old namespace reg <code>oldReg</code> interferes
+     * with what currently maps to <code>newReg</code>.
+     *
+     * @param oldReg old namespace register
+     * @param newReg new namespace register
+     * @param category category of old namespace register
+     * @return true if oldReg will interfere with newReg
+     */
+    public boolean interferes(int oldReg, int newReg, int category) {
+        if (newReg >= newRegInterference.size()) {
+            return false;
+        } else {
+            IntSet existing = newRegInterference.get(newReg);
+
+            if (existing == null) {
+                return false;
+            } else if (category == 1) {
+                return existing.has(oldReg);
+            } else {
+                return existing.has(oldReg)
+                        || (interferes(oldReg, newReg+1, category-1));
+            }
+        }
+    }
+
+    /**
+     * Checks to see if old namespace reg <code>oldReg</code> interferes
+     * with what currently maps to <code>newReg</code>.
+     *
+     * @param oldSpec non-null; old namespace register
+     * @param newReg new namespace register
+     * @return true if oldReg will interfere with newReg
+     */
+    public boolean interferes(RegisterSpec oldSpec, int newReg) {
+        return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory());
+    }
+
+    /**
+     * Adds a register's interference set to the interference list,
+     * growing it if necessary.
+     * @param newReg register in new namespace
+     * @param oldReg register in old namespace
+     */
+    private void addInterfence(int newReg, int oldReg) {
+        newRegInterference.ensureCapacity(newReg + 1);
+
+        while (newReg >= newRegInterference.size()) {
+            newRegInterference.add(new BitIntSet(newReg +1));
+        }
+
+        oldRegInterference.mergeInterferenceSet(
+                oldReg, newRegInterference.get(newReg));
+    }
+
+    /**
+     * Checks to see if any of a set of old-namespace registers are
+     * pinned to the specified new-namespace reg + category. Takes into
+     * account the category of the old-namespace registers.
+     *
+     * @param oldSpecs non-null; set of old-namespace regs
+     * @param newReg &gt;= 0 new-namespace register
+     * @param targetCategory 1 or 2; the number of adjacent new-namespace
+     * registers (starting at ropReg) to consider
+     * @return true if any of the old-namespace register have been mapped
+     * to the new-namespace register + category
+     */
+    public boolean areAnyPinned(RegisterSpecList oldSpecs,
+            int newReg, int targetCategory) {
+
+        int sz = oldSpecs.size();
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec oldSpec = oldSpecs.get(i);
+            int r = oldToNew(oldSpec.getReg());
+
+            /*
+             * If oldSpec is a category-2 register, then check both newReg
+             * and newReg - 1.
+             */
+            if (r == newReg
+                || (oldSpec.getCategory() == 2 && (r + 1) == newReg)
+                || (targetCategory == 2 && (r == newReg + 1))) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
new file mode 100644
index 0000000..ad10cd7
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.List;
+
+/**
+ * Upgrades insn to their literal (constant-immediate) equivilent if possible.
+ * Also switches IF instructions that compare with a constant zero or null
+ * to be their IF_*Z equivalents.
+ */
+public class LiteralOpUpgrader {
+
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Process a method.
+     *
+     * @param ssaMethod non-null; method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        LiteralOpUpgrader dc;
+
+        dc = new LiteralOpUpgrader(ssaMethod);
+            
+        dc.run();
+    }
+
+    private LiteralOpUpgrader(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+    }
+
+    /**
+     * Returns true if the register contains an integer 0 or a known-null
+     * object reference
+     *
+     * @param spec non-null spec
+     * @return true for 0 or null type bearers
+     */
+    private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) {
+        TypeBearer tb = spec.getTypeBearer();
+        if (tb instanceof CstLiteralBits) {
+            CstLiteralBits clb = (CstLiteralBits) tb;
+            return (clb.getLongBits() == 0);
+        }
+        return false;
+    }
+
+    /**
+     * Run the literal op upgrader
+     */
+    private void run() {
+        final TranslationAdvice advice = Optimizer.getAdvice();
+
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn(NormalSsaInsn insn) {
+                // do nothing
+            }
+
+            public void visitPhiInsn(PhiInsn insn) {
+                // do nothing
+            }
+
+            public void visitNonMoveInsn(NormalSsaInsn insn) {
+
+                Insn originalRopInsn = insn.getOriginalRopInsn();
+                Rop opcode = originalRopInsn.getOpcode();
+                RegisterSpecList sources = insn.getSources();
+
+                if (sources.size() != 2 ) {
+                    // We're only dealing with two-source insns here.
+                    return;
+                }
+
+                if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+                    /*
+                     * An if instruction can become an if-*z instruction.
+                     */
+                    if (isConstIntZeroOrKnownNull(sources.get(0))) {
+                        replacePlainInsn(insn, sources.withoutFirst(),
+                                RegOps.flippedIfOpcode(opcode.getOpcode()));
+                    } else if (isConstIntZeroOrKnownNull(sources.get(1))) {
+                        replacePlainInsn(insn, sources.withoutLast(),
+                                opcode.getOpcode());
+                    }
+                } else if (advice.hasConstantOperation(
+                        opcode, sources.get(0), sources.get(1))) {
+                    insn.upgradeToLiteral();
+                } else  if (opcode.isCommutative()
+                        && advice.hasConstantOperation(
+                        opcode, sources.get(1), sources.get(0))) {
+                    /*
+                     * An instruction can be commuted to a literal operation
+                     */
+
+                    insn.setNewSources(
+                            RegisterSpecList.make(
+                                    sources.get(1), sources.get(0)));
+
+                    insn.upgradeToLiteral();
+                }
+            }
+        });
+    }
+
+    /**
+     * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The
+     * new PlainInsn is contructed with a new RegOp and new sources.
+     *
+     * TODO move this somewhere else.
+     *
+     * @param insn non-null; an SsaInsn containing a PlainInsn
+     * @param newSources non-null; new sources list for new insn
+     * @param newOpcode A RegOp from {@link RegOps}
+     */
+    private void replacePlainInsn(NormalSsaInsn insn,
+            RegisterSpecList newSources, int newOpcode) {
+
+        Insn originalRopInsn = insn.getOriginalRopInsn();
+        Rop newRop = Rops.ropFor(newOpcode,
+                insn.getResult(), newSources, null);
+        Insn newRopInsn = new PlainInsn(newRop,
+                originalRopInsn.getPosition(), insn.getResult(),
+                newSources);
+        NormalSsaInsn newInsn
+                = new NormalSsaInsn(newRopInsn, insn.getBlock());
+
+        List<SsaInsn> insns = insn.getBlock().getInsns();
+
+        ssaMeth.onInsnRemoved(insn);
+        insns.set(insns.lastIndexOf(insn), newInsn);
+        ssaMeth.onInsnAdded(newInsn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
new file mode 100644
index 0000000..21c306b
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method. Stolen and retrofitted from
+ * com.android.dx.rop.code.LocalVariableExtractor
+ *
+ * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in,
+ * converted, and adapted through edge-splitting.
+ */
+public class LocalVariableExtractor {
+    /** non-null; method being extracted from */
+    private final SsaMethod method;
+
+    /** non-null; block list for the method */
+    private final ArrayList<SsaBasicBlock> blocks;
+
+    /** non-null; result in-progress */
+    private final LocalVariableInfo resultInfo;
+
+    /** non-null; work set indicating blocks needing to be processed */
+    private final BitSet workSet;
+
+    /**
+     * Extracts out all the local variable information from the given method.
+     *
+     * @param method non-null; the method to extract from
+     * @return non-null; the extracted information
+     */
+    public static LocalVariableInfo extract(SsaMethod method) {
+        LocalVariableExtractor lve = new LocalVariableExtractor(method);
+        return lve.doit();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #extract}.
+     *
+     * @param method non-null; the method to extract from
+     */
+    private LocalVariableExtractor(SsaMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        ArrayList<SsaBasicBlock> blocks = method.getBlocks();
+
+        this.method = method;
+        this.blocks = blocks;
+        this.resultInfo = new LocalVariableInfo(method);
+        this.workSet = new BitSet(blocks.size());
+    }
+
+    /**
+     * Does the extraction.
+     *
+     * @return non-null; the extracted information
+     */
+    private LocalVariableInfo doit() {
+
+        //FIXME why is this needed here?
+        if (method.getRegCount() > 0 ) {
+            for (int bi = method.getEntryBlockIndex();
+                 bi >= 0;
+                 bi = workSet.nextSetBit(0)) {
+                workSet.clear(bi);
+                processBlock(bi);
+            }
+        }
+
+        resultInfo.setImmutable();
+        return resultInfo;
+    }
+
+    /**
+     * Processes a single block.
+     *
+     * @param blockIndex &gt;= 0; block index of the block to process
+     */
+    private void processBlock(int blockIndex) {
+        RegisterSpecSet primaryState
+                = resultInfo.mutableCopyOfStarts(blockIndex);
+        SsaBasicBlock block = blocks.get(blockIndex);
+        List<SsaInsn> insns = block.getInsns();
+        int insnSz = insns.size();
+
+        // The exit block has no insns and no successors
+        if (blockIndex == method.getExitBlockIndex()) {
+            return;
+        }
+
+        /*
+         * We may have to treat the last instruction specially: If it
+         * can (but doesn't always) throw, and the exception can be
+         * caught within the same method, then we need to use the
+         * state *before* executing it to be what is merged into
+         * exception targets.
+         */
+        SsaInsn lastInsn = insns.get(insnSz - 1);
+        boolean hasExceptionHandlers
+                = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ;
+        boolean canThrowDuringLastInsn = hasExceptionHandlers
+                && (lastInsn.getResult() != null);
+        int freezeSecondaryStateAt = insnSz - 1;
+        RegisterSpecSet secondaryState = primaryState;
+
+        /*
+         * Iterate over the instructions, adding information for each place
+         * that the active variable set changes.
+         */
+
+        for (int i = 0; i < insnSz; i++) {
+            if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+                // Until this point, primaryState == secondaryState.
+                primaryState.setImmutable();
+                primaryState = primaryState.mutableCopy();
+            }
+
+            SsaInsn insn = insns.get(i);
+            RegisterSpec result;
+
+            result = insn.getLocalAssignment();
+
+            if (result == null) {
+                // We may be nuking an existing local
+
+                result = insn.getResult();
+
+                if (result != null && primaryState.get(result.getReg()) != null) {
+                    primaryState.remove(primaryState.get(result.getReg()));
+                }
+                continue;
+            }
+
+            result = result.withSimpleType();
+
+            RegisterSpec already = primaryState.get(result);
+            /*
+             * The equals() check ensures we only add new info if
+             * the instruction causes a change to the set of
+             * active variables.
+             */
+            if (!result.equals(already)) {
+                /*
+                 * If this insn represents a local moving from one register
+                 * to another, remove the association between the old register
+                 * and the local.
+                 */
+                RegisterSpec previous
+                        = primaryState.localItemToSpec(result.getLocalItem());
+
+                if (previous != null
+                        && (previous.getReg() != result.getReg())) {
+
+                    primaryState.remove(previous);
+                }
+
+                resultInfo.addAssignment(insn, result);
+                primaryState.put(result);
+            }
+        }
+
+        primaryState.setImmutable();
+
+        /*
+         * Merge this state into the start state for each successor,
+         * and update the work set where required (that is, in cases
+         * where the start state for a block changes).
+         */
+
+        IntList successors = block.getSuccessorList();
+        int succSz = successors.size();
+        int primarySuccessor = block.getPrimarySuccessorIndex();
+
+        for (int i = 0; i < succSz; i++) {
+            int succ = successors.get(i);
+            RegisterSpecSet state = (succ == primarySuccessor) ?
+                primaryState : secondaryState;
+
+            if (resultInfo.mergeStarts(succ, state)) {
+                workSet.set(succ);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
new file mode 100644
index 0000000..f7c37d2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Container for local variable information for a particular {@link
+ * com.android.dx.ssa.SsaMethod}.
+ * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
+ */
+public class LocalVariableInfo         extends MutabilityControl {
+    /** &gt;= 0; the register count for the method */
+    private final int regCount;
+
+    /**
+     * non-null; {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block
+     * that has no locals; it is empty and immutable but has an appropriate
+     * max size for the method
+     */
+    private final RegisterSpecSet emptySet;
+
+    /**
+     * non-null; array consisting of register sets representing the
+     * sets of variables already assigned upon entry to each block,
+     * where array indices correspond to block indices
+     */
+    private final RegisterSpecSet[] blockStarts;
+
+    /** non-null; map from instructions to the variable each assigns */
+    private final HashMap<SsaInsn, RegisterSpec> insnAssignments;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method non-null; the method being represented by this instance
+     */
+    public LocalVariableInfo(SsaMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        List<SsaBasicBlock> blocks = method.getBlocks();
+
+        this.regCount = method.getRegCount();
+        this.emptySet = new RegisterSpecSet(regCount);
+        this.blockStarts = new RegisterSpecSet[blocks.size()];
+        this.insnAssignments =
+            new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);
+
+        emptySet.setImmutable();
+    }
+
+    /**
+     * Sets the register set associated with the start of the block with
+     * the given index.
+     *
+     * @param index &gt;= 0; the block index
+     * @param specs non-null; the register set to associate with the block
+     */
+    public void setStarts(int index, RegisterSpecSet specs) {
+        throwIfImmutable();
+
+        if (specs == null) {
+            throw new NullPointerException("specs == null");
+        }
+
+        try {
+            blockStarts[index] = specs;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus index");
+        }
+    }
+
+    /**
+     * Merges the given register set into the set for the block with the
+     * given index. If there was not already an associated set, then this
+     * is the same as calling {@link #setStarts}. Otherwise, this will
+     * merge the two sets and call {@link #setStarts} on the result of the
+     * merge.
+     *
+     * @param index &gt;= 0; the block index
+     * @param specs non-null; the register set to merge into the start set
+     * for the block
+     * @return <code>true</code> if the merge resulted in an actual change
+     * to the associated set (including storing one for the first time) or
+     * <code>false</code> if there was no change
+     */
+    public boolean mergeStarts(int index, RegisterSpecSet specs) {
+        RegisterSpecSet start = getStarts0(index);
+        boolean changed = false;
+
+        if (start == null) {
+            setStarts(index, specs);
+            return true;
+        }
+
+        RegisterSpecSet newStart = start.mutableCopy();
+        newStart.intersect(specs, true);
+
+        if (start.equals(newStart)) {
+            return false;
+        }
+
+        newStart.setImmutable();
+        setStarts(index, newStart);
+
+        return true;
+    }
+
+    /**
+     * Gets the register set associated with the start of the block
+     * with the given index. This returns an empty set with the appropriate
+     * max size if no set was associated with the block in question.
+     *
+     * @param index &gt;= 0; the block index
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet getStarts(int index) {
+        RegisterSpecSet result = getStarts0(index);
+
+        return (result != null) ? result : emptySet;
+    }
+
+    /**
+     * Gets the register set associated with the start of the given
+     * block. This is just convenient shorthand for
+     * <code>getStarts(block.getLabel())</code>.
+     *
+     * @param block non-null; the block in question
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet getStarts(SsaBasicBlock block) {
+        return getStarts(block.getIndex());
+    }
+
+    /**
+     * Gets a mutable copy of the register set associated with the
+     * start of the block with the given index. This returns a
+     * newly-allocated empty {@link RegisterSpecSet} of appropriate
+     * max size if there is not yet any set associated with the block.
+     *
+     * @param index &gt;= 0; the block index
+     * @return non-null; the associated register set
+     */
+    public RegisterSpecSet mutableCopyOfStarts(int index) {
+        RegisterSpecSet result = getStarts0(index);
+
+        return (result != null) ?
+            result.mutableCopy() : new RegisterSpecSet(regCount);
+    }
+
+    /**
+     * Adds an assignment association for the given instruction and
+     * register spec. This throws an exception if the instruction
+     * doesn't actually perform a named variable assignment.
+     *
+     * <b>Note:</b> Although the instruction contains its own spec for
+     * the result, it still needs to be passed in explicitly to this
+     * method, since the spec that is stored here should always have a
+     * simple type and the one in the instruction can be an arbitrary
+     * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value).
+     *
+     * @param insn non-null; the instruction in question
+     * @param spec non-null; the associated register spec
+     */
+    public void addAssignment(SsaInsn insn, RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (insn == null) {
+            throw new NullPointerException("insn == null");
+        }
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        insnAssignments.put(insn, spec);
+    }
+
+    /**
+     * Gets the named register being assigned by the given instruction, if
+     * previously stored in this instance.
+     *
+     * @param insn non-null; instruction in question
+     * @return null-ok; the named register being assigned, if any
+     */
+    public RegisterSpec getAssignment(SsaInsn insn) {
+        return insnAssignments.get(insn);
+    }
+
+    /**
+     * Gets the number of assignments recorded by this instance.
+     *
+     * @return &gt;= 0; the number of assignments
+     */
+    public int getAssignmentCount() {
+        return insnAssignments.size();
+    }
+
+    public void debugDump() {
+        for (int index = 0 ; index < blockStarts.length; index++) {
+            if (blockStarts[index] == null) {
+                continue;
+            }
+
+            if (blockStarts[index] == emptySet) {
+                System.out.printf("%04x: empty set\n", index);
+            } else {
+                System.out.printf("%04x: %s\n", index, blockStarts[index]);
+            }
+        }
+    }
+
+    /**
+     * Helper method, to get the starts for a index, throwing the
+     * right exception for range problems.
+     *
+     * @param index &gt;= 0; the block index
+     * @return null-ok; associated register set or <code>null</code> if there
+     * is none
+     */
+    private RegisterSpecSet getStarts0(int index) {
+        try {
+            return blockStarts[index];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus index");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
new file mode 100644
index 0000000..a27aec5
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.CstInteger;
+
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Combine identical move-param insns, which may result from Ropper's
+ * handling of synchronized methods.
+ */
+public class MoveParamCombiner {
+
+    /** method to process */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Processes a method with this optimization step.
+     * 
+     * @param ssaMethod method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        new MoveParamCombiner(ssaMethod).run();
+    }
+
+    private MoveParamCombiner(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;        
+    }
+
+    /**
+     * Runs this optimization step.
+     */
+    private void run() {
+        // This will contain the definition specs for each parameter
+        final RegisterSpec[] paramSpecs
+                = new RegisterSpec[ssaMeth.getParamWidth()];
+
+        // Insns to delete when all done
+        final HashSet<SsaInsn> deletedInsns = new HashSet();
+
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn (NormalSsaInsn insn) {
+            }
+            public void visitPhiInsn (PhiInsn phi) {
+            }
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) {
+                    return;
+                }
+
+                int param = getParamIndex(insn);
+
+                if (paramSpecs[param] == null) {
+                    paramSpecs[param] = insn.getResult();
+                } else {
+                    final RegisterSpec specA = paramSpecs[param];
+                    final RegisterSpec specB = insn.getResult();
+                    LocalItem localA = specA.getLocalItem();
+                    LocalItem localB = specB.getLocalItem();
+                    LocalItem newLocal;
+
+                    /*
+                     * Is there local information to preserve?
+                     */
+
+                    if (localA == null) {
+                        newLocal = localB;
+                    } else if (localB == null) {
+                        newLocal = localA;
+                    } else if (localA.equals(localB)) {
+                        newLocal = localA;
+                    } else {
+                        /*
+                         * Oddly, these two identical move-params have distinct
+                         * debug info. We'll just keep them distinct.
+                         */
+                        return;
+                    }
+
+                    ssaMeth.getDefinitionForRegister(specA.getReg())
+                            .setResultLocal(newLocal);
+
+                    /*
+                     * Map all uses of specB to specA
+                     */
+
+                    RegisterMapper mapper = new RegisterMapper() {
+                        /** @inheritDoc */
+                        public int getNewRegisterCount() {
+                            return ssaMeth.getRegCount();
+                        }
+
+                        /** @inheritDoc */
+                        public RegisterSpec map(RegisterSpec registerSpec) {
+                            if (registerSpec.getReg() == specB.getReg()) {
+                                return specA;
+                            }
+
+                            return registerSpec;
+                        }
+                    };
+
+                    List<SsaInsn> uses
+                            = ssaMeth.getUseListForRegister(specB.getReg());
+
+                    // Use list is modified by mapSourceRegisters
+                    for (int i = uses.size() - 1; i >= 0; i--) {
+                        SsaInsn use = uses.get(i);                                
+                        use.mapSourceRegisters(mapper);
+                    }
+
+                    deletedInsns.add(insn);
+                }
+
+            }
+        });
+
+        ssaMeth.deleteInsns(deletedInsns);
+    }
+
+    /**
+     * Returns the parameter index associated with a move-param insn. Does
+     * not verify that the insn is a move-param insn.
+     *
+     * @param insn non-null; a move-param insn
+     * @return &gt;=0 parameter index
+     */
+    private int getParamIndex(NormalSsaInsn insn) {
+        CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn());
+
+        int param = ((CstInteger)cstInsn.getConstant()).getValue();
+        return param;
+    }
+
+}
diff --git a/dx/src/com/android/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
new file mode 100644
index 0000000..ad9315a
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+
+/**
+ * A "normal" (non-phi) instruction in SSA form. Always wraps a ROP insn.
+ */
+public final class NormalSsaInsn extends SsaInsn implements Cloneable {
+
+    /**
+     * ROP insn that we're wrapping
+     */
+    private Insn insn;
+
+    /**
+     * Creates an instance.
+     *
+     * @param insn Rop insn to wrap
+     * @param block block that contains this insn
+     */
+    NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
+        super(block);
+        this.insn = insn;
+        this.result = insn.getResult();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void mapSourceRegisters(RegisterMapper mapper) {
+
+        RegisterSpecList oldSources = insn.getSources();
+        RegisterSpecList newSources = mapper.map(oldSources);
+
+        if (newSources != oldSources) {
+            insn = insn.withNewRegisters(result, newSources);
+            block.getParent().onSourcesChanged(this, oldSources);
+        }
+    }
+
+    /**
+     * Changes one of the insn's sources. New source should be of same type
+     * and category.
+     *
+     * @param index &gt;=0; index of source to change
+     * @param newSpec spec for new source
+     */
+    public final void changeOneSource(int index, RegisterSpec newSpec) {
+        RegisterSpecList origSources = insn.getSources();
+        int sz = origSources.size();
+        RegisterSpecList newSources = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            newSources.set(i, i == index ? newSpec : origSources.get(i));
+        }
+        newSources.setImmutable();
+
+        RegisterSpec origSpec = origSources.get(index);
+        if (origSpec.getReg() != newSpec.getReg()) {
+            /*
+             * If the register remains unchanged, we're only changing 
+             * the type or local var name so don't update use list
+             */
+            block.getParent().onSourceChanged(this, origSpec, newSpec);
+        }
+
+        insn = insn.withNewRegisters(result, newSources);
+    }
+
+    /**
+     * Changes the source list of the insn. New source list should be the
+     * same size and consist of sources of identical types.
+     *
+     * @param newSources non-null new sources list.
+     */
+    public final void setNewSources (RegisterSpecList newSources) {
+        RegisterSpecList origSources = insn.getSources();
+        if (origSources.size() != newSources.size()) {
+            throw new RuntimeException("Sources counts don't match");
+        }
+
+        insn = insn.withNewRegisters(result, newSources);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NormalSsaInsn clone() {
+        return (NormalSsaInsn)super.clone();
+    }
+
+    /**
+     * Like rop.Insn.getSources()
+     * @return null-ok; sources list
+     */
+    public RegisterSpecList getSources() {
+        return insn.getSources();
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toRopInsn().toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn toRopInsn() {
+        return insn.withNewRegisters(result,insn.getSources());
+    }
+
+    /**
+     * @return the Rop opcode for this insn
+     */
+    @Override
+    public Rop getOpcode() {
+        return insn.getOpcode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn getOriginalRopInsn() {
+        return insn;
+    }
+
+    /** {@inheritDoc} */
+    public RegisterSpec getLocalAssignment() {
+        RegisterSpec assignment;
+        
+        if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+            assignment = insn.getSources().get(0);
+        } else {
+            assignment = result;
+        }
+
+        if (assignment == null) {
+            return null;
+        }
+
+        LocalItem local = assignment.getLocalItem();
+
+        if (local == null) {
+            return null;
+        }
+
+        return assignment;
+    }
+
+    /**
+     * Upgrades this insn to a version that represents the constant last
+     * source literally. If the upgrade is not possible, this does nothing.
+     *
+     * @see Insn#withLastSourceLiteral
+     */
+    public void upgradeToLiteral() {
+        RegisterSpecList oldSources = insn.getSources();
+        insn = insn.withLastSourceLiteral();
+        block.getParent().onSourcesChanged(this, oldSources);
+    }
+
+    /**
+     * @return true if this is a move (but not a move-operand) instruction
+     */
+    @Override
+    public boolean isNormalMoveInsn() {
+        return insn.getOpcode().getOpcode() == RegOps.MOVE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isMoveException() {
+        return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean canThrow() {
+        return insn.canThrow();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor v) {
+        if (isNormalMoveInsn()) {
+            v.visitMoveInsn(this);
+        } else {
+            v.visitNonMoveInsn(this);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public  boolean isPhiOrMove() {
+        return isNormalMoveInsn();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * TODO increase the scope of this.
+     */
+    @Override
+    public boolean hasSideEffect() {
+        Rop opcode = getOpcode();
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            return true;
+        }
+
+        boolean hasLocalSideEffect
+                = Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+
+        switch (opcode.getOpcode()) {
+            case RegOps.MOVE_RESULT:
+            case RegOps.MOVE:
+            case RegOps.CONST:
+                return hasLocalSideEffect;
+            default:
+                return true;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/Optimizer.java b/dx/src/com/android/dx/ssa/Optimizer.java
new file mode 100644
index 0000000..cee6d7b
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Optimizer.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.back.SsaToRop;
+import com.android.dx.ssa.back.LivenessAnalyzer;
+
+import java.util.EnumSet;
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Runs a method through the SSA form conversion, any optimization algorithms,
+ * and returns it to rop form.
+ */
+public class Optimizer {
+    private static boolean preserveLocals = true;
+
+    private static TranslationAdvice advice;
+
+    /** optional optimizer steps */
+    public enum OptionalStep {
+        MOVE_PARAM_COMBINER,SCCP,LITERAL_UPGRADE,CONST_COLLECTOR
+    }
+
+    /**
+     * @return true if local variable information should be preserved, even
+     * at code size/register size cost
+     */
+    public static boolean getPreserveLocals() {
+        return preserveLocals;
+    }
+
+    /**
+     * @return non-null; translation advice
+     */
+    public static TranslationAdvice getAdvice() {
+        return advice;
+    }
+
+    /**
+     * Runs optimization algorthims over this method, and returns a new
+     * instance of RopMethod with the changes.
+     *
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param inPreserveLocals true if local variable info should be preserved,
+     * at the cost of some registers and insns
+     * @param inAdvice non-null; translation advice
+     * @return optimized method
+     */
+    public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice, 
+                EnumSet.allOf(OptionalStep.class));
+    }
+
+    /**
+     * Runs optimization algorthims over this method, and returns a new
+     * instance of RopMethod with the changes.
+     * 
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param inPreserveLocals true if local variable info should be preserved,
+     * at the cost of some registers and insns
+     * @param inAdvice non-null; translation advice
+     * @param steps set of optional optimization steps to run
+     * @return optimized method
+     */
+    public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+        SsaMethod ssaMeth = null;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+        runSsaFormSteps(ssaMeth, steps);
+
+        RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false);
+
+        if (resultMeth.getBlocks().getRegCount()
+                > advice.getMaxOptimalRegisterCount()) {
+            // Try to see if we can squeeze it under the register count bar
+            resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic,
+                    steps);
+        }
+        return resultMeth;
+    }
+
+    /**
+     * Runs the optimizer with a strategy to minimize the number of rop-form
+     * registers used by the end result. Dex bytecode does not have instruction
+     * forms that take register numbers larger than 15 for all instructions.
+     * If we've produced a method that uses more than 16 registers, try again
+     * with a different strategy to see if we can get under the bar. The end
+     * result will be much more efficient.
+     *
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param steps set of optional optimization steps to run
+     * @return optimized method
+     */
+    private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth,
+            int paramWidth, boolean isStatic,
+            EnumSet<OptionalStep> steps) {
+        SsaMethod ssaMeth;
+        RopMethod resultMeth;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(
+                rmeth, paramWidth, isStatic);
+
+        EnumSet<OptionalStep> newSteps = steps.clone();
+
+        /*
+         * CONST_COLLECTOR trades insns for registers, which is not an
+         * appropriate strategy here.
+         */
+        newSteps.remove(OptionalStep.CONST_COLLECTOR);
+
+        runSsaFormSteps(ssaMeth, newSteps);
+
+        resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true);
+        return resultMeth;
+    }
+
+    private static void runSsaFormSteps(SsaMethod ssaMeth,
+            EnumSet<OptionalStep> steps) {
+        boolean needsDeadCodeRemover = true;
+
+        if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) {
+            MoveParamCombiner.process(ssaMeth);
+        }
+
+        if (steps.contains(OptionalStep.SCCP)) {
+            SCCP.process(ssaMeth);
+        }
+
+        if (steps.contains(OptionalStep.LITERAL_UPGRADE)) {
+            LiteralOpUpgrader.process(ssaMeth);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        if (steps.contains(OptionalStep.CONST_COLLECTOR)) {
+            ConstCollector.process(ssaMeth);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        // dead code remover must be run before phi type resolver
+        if (needsDeadCodeRemover) {
+            DeadCodeRemover.process(ssaMeth);
+        }
+
+        PhiTypeResolver.process(ssaMeth);
+    }
+
+    public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugDeadCodeRemover(RopMethod rmeth,
+            int paramWidth, boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        SsaMethod ssaMeth;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+        DeadCodeRemover.process(ssaMeth);
+
+        return ssaMeth;
+    }
+
+    public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth,
+            int paramWidth, boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+
+        SsaMethod ssaMeth;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+
+        runSsaFormSteps(ssaMeth, steps);
+
+        LivenessAnalyzer.constructInterferenceGraph(ssaMeth);
+
+        return ssaMeth;        
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiInsn.java b/dx/src/com/android/dx/ssa/PhiInsn.java
new file mode 100644
index 0000000..1829133
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiInsn.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Phi instruction (magical post-control-flow-merge) instruction
+ * in SSA form. Will be converted to moves in predecessor blocks before
+ * conversion back to ROP form.
+ */
+public final class PhiInsn extends SsaInsn {
+
+    /**
+     * the original result register of the phi insn is needed during the
+     * renaming process after the new result register has already been chosen.
+     */
+    private int ropResultReg;
+    private ArrayList<Operand> operands = new ArrayList<Operand>();
+    private RegisterSpecList sources;
+
+    /**
+     * A single phi operand, consiting of source register and block index
+     * for move.
+     */
+    class Operand {
+        RegisterSpec regSpec;
+        int blockIndex;
+        int ropLabel;       //mostly for debugging
+
+        Operand (final RegisterSpec regSpec, final int blockIndex,
+                final int ropLabel){
+            this.regSpec = regSpec;
+            this.blockIndex = blockIndex;
+            this.ropLabel = ropLabel;
+        }
+    }
+
+    public static interface Visitor {
+        public void visitPhiInsn(PhiInsn insn);
+    }
+
+    public PhiInsn clone() {
+        throw new UnsupportedOperationException("can't clone phi");
+    }
+
+    /**
+     * Constructs a new phi insn with no operands.
+     * @param resultReg the result reg for this phi insn
+     * @param block block containing this insn.
+     */
+    PhiInsn(final RegisterSpec resultReg, final SsaBasicBlock block) {
+        super(block);
+        this.result = resultReg;
+        ropResultReg = resultReg.getReg();
+    }
+
+    /**
+     * Makes a phi insn with a void result type.
+     * @param resultReg the result register for this phi insn.
+     * @param block block containing this insn.
+     */
+    PhiInsn(final int resultReg, final SsaBasicBlock block) {
+        super(block);
+
+        /*
+         * The type here is bogus: the type depends on the operand and
+         * will be derived later.
+         */
+        this.result = RegisterSpec.make(resultReg, Type.VOID);
+        ropResultReg = resultReg;
+    }
+
+    /**
+     * Updates the TypeBearers of all the sources (phi operands) to be
+     * the current TypeBearer of the register-defining instruction's result.
+     * This is used during phi-type resolution.<p>
+     *
+     * Note that local association of operands are preserved in this step.
+     *
+     * @param ssaMeth method that contains this insn
+     */
+    void updateSourcesToDefinitions(SsaMethod ssaMeth) {
+
+        for (Operand o: operands) {
+            RegisterSpec def 
+                = ssaMeth.getDefinitionForRegister(
+                    o.regSpec.getReg()).getResult();
+
+            o.regSpec = o.regSpec.withType(def.getType());
+        }
+
+        sources = null;
+    }
+
+    /**
+     * Changes the result type. Used during phi type resolution
+     *
+     * @param type non-null; new TypeBearer
+     * @param local null-ok; new local info, if available
+     */
+    void changeResultType(TypeBearer type, LocalItem local) {
+        result = RegisterSpec.makeLocalOptional(result.getReg(), type, local);
+    }
+
+    /**
+     * @return the original rop-form result reg. Useful during renaming.
+     */
+    int getRopResultReg() {
+        return ropResultReg;
+    }
+
+    /**
+     * Add an operand to this phi instruction
+     * @param registerSpec register spec, including type and reg of operand
+     * @param predBlock Predecessor block to be associated with this operand
+     */
+    public void addPhiOperand(RegisterSpec registerSpec,
+            SsaBasicBlock predBlock) {
+        operands.add(new Operand(registerSpec, predBlock.getIndex(),
+                predBlock.getRopLabel()));
+        
+        // in case someone has already called getSources()
+        sources = null;
+    }
+
+    /**
+     * Gets the index of the pred block associated with the RegisterSpec
+     * at the particular getSources() index.
+     * @param sourcesIndex index of source in getSources()
+     * @return block index
+     */
+    public int predBlockIndexForSourcesIndex(int sourcesIndex) {
+        return operands.get(sourcesIndex).blockIndex;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns null for <code>PhiInsn</code>s
+     */
+    @Override
+    public Rop getOpcode() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns null for <code>PhiInsn</code>s
+     */
+    @Override
+    public Insn getOriginalRopInsn() {
+        return null;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns false for <code>PhiInsn</code>s
+     */
+    @Override
+    public boolean canThrow() {
+        return false;
+    }
+
+    /**
+     * Gets sources. Constructed lazily from phi operand data structures and
+     * then cached.
+     * @return sources list
+     */
+    public RegisterSpecList getSources() {
+
+        if (sources != null) {
+            return sources;
+        }
+
+        if (operands.size() == 0) {
+            // How'd this happen? A phi insn with no operand?
+            return RegisterSpecList.EMPTY;
+        }
+
+        int szSources = operands.size();
+        sources = new RegisterSpecList(szSources);
+
+        for (int i = 0; i < szSources; i++) {
+            Operand o = operands.get(i);
+
+            sources.set(i, o.regSpec);
+        }
+
+        sources.setImmutable();
+        return sources;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isRegASource(int reg) {
+        /*
+         * Avoid creating a sources list in case it has not already been
+         * created
+         */
+
+        for (Operand o: operands) {
+            if (o.regSpec.getReg() == reg) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @return true if all operands use the same register
+     */
+    public boolean areAllOperandsEqual() {
+        if (operands.size() == 0 ) {
+            // this should never happen
+            return true;
+        }
+
+        int firstReg = operands.get(0).regSpec.getReg();
+        for (Operand o: operands) {
+            if (firstReg != o.regSpec.getReg()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void mapSourceRegisters(RegisterMapper mapper) {
+        for (Operand o: operands) {
+            RegisterSpec old = o.regSpec;
+            o.regSpec = mapper.map(old);
+            if (old != o.regSpec) {
+                block.getParent().onSourceChanged(this, old, o.regSpec);
+            }
+        }
+        sources = null;
+    }
+
+    /**
+     * Always throws an exeption, since
+     * a phi insn may not be converted back to rop form
+     * @return always throws exception
+     */
+    @Override
+    public Insn toRopInsn() {
+        throw new IllegalArgumentException(
+                "Cannot convert phi insns to rop form");
+    }
+
+    /**
+     * Returns the list of predecessor blocks associated with all operands
+     * that have <code>reg</code> as an operand register.
+     *
+     * @param reg register to look up
+     * @param ssaMeth method we're operating on
+     * @return List of predecessor blocks, empty if none
+     */
+    public List<SsaBasicBlock> predBlocksForReg (int reg, SsaMethod ssaMeth) {
+        ArrayList<SsaBasicBlock> ret 
+            = (ArrayList<SsaBasicBlock>)new ArrayList();
+
+        for (Operand o: operands) {
+            if (o.regSpec.getReg() == reg) {
+                ret.add(ssaMeth.getBlocks().get(o.blockIndex));
+            }
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public  boolean isPhiOrMove() {
+        return true;    
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean hasSideEffect() {
+        return Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(SsaInsn.Visitor v) {
+        v.visitPhiInsn(this);
+    }
+
+    /**
+     * @return human-readable string for listing dumps
+     */
+    public String toHuman() {
+        return toHumanWithInline(null);
+    }
+
+    /**
+     * Returns human-readable string for listing dumps.
+     * Allows sub-classes to specify extra text
+     * @param extra null-ok; the argument to print after the opcode
+     * @return human-readable string for listing dumps
+     */
+    protected final String toHumanWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append(SourcePosition.NO_INFO);
+        sb.append(": ");
+        sb.append("phi");       
+
+        if (extra != null) {
+            sb.append("(");
+            sb.append(extra);
+            sb.append(")");
+        }
+
+        if (result == null) {
+            sb.append(" .");
+        } else {
+            sb.append(" ");
+            sb.append(result.toHuman());
+        }
+
+        sb.append(" <-");
+
+        int sz = getSources().size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(" ");
+                sb.append(sources.get(i).toHuman()
+                        + "[b="
+                        + Hex.u2(operands.get(i).ropLabel)  + "]");
+            }
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiTypeResolver.java b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
new file mode 100644
index 0000000..f4c26d7
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.cf.code.Merger;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Resolves the result types of phi instructions. When phi instructions
+ * are inserted, their result types are set to BT_VOID (which is a nonsensical
+ * type for a register) but must be resolve to a real type before converting
+ * out of SSA form.<p>
+ *
+ * The resolve is done as an iterative merge of each phi's operand types.
+ * Phi operands may be themselves be the result of unresolved phis,
+ * and the algorithm tries to find the most-fit type (for example, if every
+ * operand is the same constant value or the same local variable info, we want
+ * that to be reflected).<p>
+ *
+ * This algorithm assumes a dead-code remover has already removed all
+ * circular-only phis that may have been inserted.
+ */
+public class PhiTypeResolver {
+
+    SsaMethod ssaMeth;
+    /** indexed by register; all registers still defined by unresolved phis */
+    private final BitSet worklist;
+
+    /**
+     * Resolves all phi types in the method
+     * @param ssaMeth method to process
+     */
+    public static void process (SsaMethod ssaMeth) {
+        new PhiTypeResolver(ssaMeth).run();
+    }
+
+    private PhiTypeResolver(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+        worklist = new BitSet(ssaMeth.getRegCount());
+    }
+
+    /**
+     * Runs the phi-type resolver.
+     */
+    private void run() {
+
+        int regCount = ssaMeth.getRegCount();
+
+        for (int reg = 0; reg < regCount; reg++) {
+            SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg);
+
+            if (definsn != null
+                    && (definsn.getResult().getBasicType() == Type.BT_VOID)) {
+                worklist.set(reg);
+            }
+        }
+
+        int reg;
+        while ( 0 <= (reg = worklist.nextSetBit(0))) {
+            worklist.clear(reg);
+
+            /*
+             * definitions on the worklist have a type of BT_VOID, which
+             * must have originated from a PhiInsn.
+             */
+            PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg);
+
+            if (resolveResultType(definsn)) {
+                /*
+                 * If the result type has changed, re-resolve all phis
+                 * that use this.
+                 */
+
+                List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg);
+
+                int sz = useList.size();
+                for (int i = 0; i < sz; i++ ) {
+                    SsaInsn useInsn = useList.get(i);
+                    RegisterSpec resultReg = useInsn.getResult();
+                    if (resultReg != null && useInsn instanceof PhiInsn) {
+                        worklist.set(resultReg.getReg());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if a and b are equal, whether
+     * or not either of them are null.
+     * @param a
+     * @param b
+     * @return true if equal
+     */
+    private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) {
+        return (a == b) || ((a != null) && a.equals(b));
+    }
+
+    /**
+     * Resolves the result of a phi insn based on its operands. The "void"
+     * type, which is a nonsensical type for a register, is used for
+     * registers defined by as-of-yet-unresolved phi operations.
+     * 
+     * @return true if the result type changed, false if no change
+     */
+    boolean resolveResultType(PhiInsn insn) {
+        insn.updateSourcesToDefinitions(ssaMeth);
+
+        RegisterSpecList sources = insn.getSources();
+
+        // Start by finding the first non-void operand
+        RegisterSpec first = null;
+        int firstIndex = -1;
+
+        int szSources = sources.size();
+        for (int i = 0 ; i <szSources ; i++) {
+            RegisterSpec rs = sources.get(i);
+
+            if (rs.getBasicType() != Type.BT_VOID) {
+                first = rs;
+                firstIndex = i;
+            }
+        }
+
+        if (first == null) {
+            // All operands are void -- we're not ready to resolve yet
+            return false;
+        }
+
+        LocalItem firstLocal = first.getLocalItem();
+        TypeBearer mergedType = first.getType();
+        boolean sameLocals = true;
+        for (int i = 0 ; i < szSources ; i++) {
+            if (i == firstIndex) {
+                continue;
+            }
+
+            RegisterSpec rs = sources.get(i);
+
+            // Just skip void (unresolved phi results) for now
+            if (rs.getBasicType() == Type.BT_VOID){
+                continue;
+            }
+
+            sameLocals = sameLocals
+                    && equalsHandlesNulls(firstLocal, rs.getLocalItem());
+
+            mergedType = Merger.mergeType(mergedType, rs.getType());
+        }
+
+        TypeBearer newResultType;
+
+        if (mergedType != null) {
+            newResultType = mergedType;
+        } else {
+            StringBuilder sb = new StringBuilder();
+
+            for (int i = 0; i < szSources; i++) {
+                sb.append(sources.get(i).toString());
+                sb.append(' ');
+            }
+
+            throw new RuntimeException ("Couldn't map types in phi insn:" + sb);
+        }
+
+        LocalItem newLocal = sameLocals ? firstLocal : null;
+        
+        RegisterSpec result = insn.getResult();
+
+        if ((result.getTypeBearer() == newResultType)
+                && equalsHandlesNulls(newLocal, result.getLocalItem())) {
+            return false;
+        }
+
+        insn.changeResultType(newResultType, newLocal);
+
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/RegisterMapper.java b/dx/src/com/android/dx/ssa/RegisterMapper.java
new file mode 100644
index 0000000..98503e2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/RegisterMapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Represents a mapping between two register numbering schemes.
+ * Subclasses of this may be mutable, and as such the mapping provided is only
+ * valid for the lifetime of the method call in which instances of this class
+ * are passed.
+ */
+public abstract class RegisterMapper {
+
+    /**
+     * Gets the count of registers (really, the total register width, since
+     * category width is counted) in the new namespace.
+     * @return >= 0 width of new namespace.
+     */
+    public abstract int getNewRegisterCount();
+
+    /**
+     * @param registerSpec old register
+     * @return register in new space
+     */
+    public abstract RegisterSpec map(RegisterSpec registerSpec);
+
+    /**
+     *
+     * @param sources old register list
+     * @return new mapped register list, or old if nothing has changed.
+     */
+    public final RegisterSpecList map(RegisterSpecList sources) {
+        RegisterSpecList newSources;
+
+        newSources = new RegisterSpecList(sources.size());
+
+        int sz = sources.size();
+        for (int i = 0; i < sz; i++) {
+            newSources.set(i, map(sources.get(i)));
+        }
+
+        newSources.setImmutable();
+        // Return the old sources if nothing has changed
+        return newSources.equals(sources)? sources: newSources;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SCCP.java b/dx/src/com/android/dx/ssa/SCCP.java
new file mode 100644
index 0000000..1d95da6
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SCCP.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A small variant of Wegman and Zadeck's Sparse Conditional Constant
+ * Propagation algorithm.
+ */
+public class SCCP {
+    /** Lattice values  */
+    private static final int TOP = 0;
+    private static final int CONSTANT = 1;
+    private static final int VARYING = 2;
+    /** method we're processing */
+    private SsaMethod ssaMeth;
+    /** ssaMeth.getRegCount() */
+    private int regCount;
+    /** Lattice values for each SSA register */
+    private int[] latticeValues;
+    /** For those registers that are constant, this is the constant value */
+    private Constant[] latticeConstants;
+    /** Worklist of basic blocks to be processed */
+    private ArrayList<SsaBasicBlock> cfgWorklist;
+    /** Bitset containing bits for each block that has been found executable */
+    private BitSet executableBlocks;
+    /** Worklist for SSA edges.  This is a list of registers to process */
+    private ArrayList<SsaInsn> ssaWorklist;
+    /**
+     * Worklist for SSA edges that represent varying values.  It makes the
+     * algorithm much faster if you move all values to VARYING as fast as
+     * possible.
+     */
+    private ArrayList<SsaInsn> varyingWorklist;
+
+    private SCCP(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+        this.regCount = ssaMeth.getRegCount();
+        this.latticeValues = new int[this.regCount];
+        this.latticeConstants = new Constant[this.regCount];
+        this.cfgWorklist = new ArrayList<SsaBasicBlock>();
+        this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
+        this.ssaWorklist = new ArrayList<SsaInsn>();
+        this.varyingWorklist = new ArrayList<SsaInsn>();
+        for (int i = 0; i < this.regCount; i++) {
+            latticeValues[i] = TOP;
+            latticeConstants[i] = null;
+        }
+    }
+
+    /**
+     * Performs sparse conditional constant propagation on a method.
+     * @param ssaMethod Method to process
+     */
+    public static void process (SsaMethod ssaMethod) {
+        new SCCP(ssaMethod).run();
+    }
+
+    /**
+     * Add a new SSA basic block to the CFG worklist
+     * @param ssaBlock Block to add
+     */
+    private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
+        if (!executableBlocks.get(ssaBlock.getIndex())) {
+            cfgWorklist.add(ssaBlock);
+            executableBlocks.set(ssaBlock.getIndex());
+        }
+    }
+
+    /**
+     * Adds an SSA register's uses to the SSA worklist.
+     * @param reg SSA register
+     * @param latticeValue new lattice value for @param reg.
+     */
+    private void addUsersToWorklist(int reg, int latticeValue) {
+        if (latticeValue == VARYING) {
+            for (SsaInsn insn: ssaMeth.getUseListForRegister(reg)) {
+                varyingWorklist.add(insn);
+            }
+        } else {
+            for (SsaInsn insn: ssaMeth.getUseListForRegister(reg)) {
+                ssaWorklist.add(insn);
+            }
+        }
+    }
+
+    /**
+     * Sets a lattice value for a register to value.
+     * @param reg SSA register
+     * @param value Lattice value
+     * @param cst Constant value (may be null)
+     * @return true if the lattice value changed.
+     */
+    private boolean setLatticeValueTo(int reg, int value, Constant cst) {
+        if (value != CONSTANT) {
+            if (latticeValues[reg] != value) {
+                latticeValues[reg] = value;
+                return true;
+            }
+            return false;
+        } else {
+            if (latticeValues[reg] != value
+                    || !latticeConstants[reg].equals(cst)) {
+                latticeValues[reg] = value;
+                latticeConstants[reg] = cst;
+                return true;
+            }
+            return false;
+        }
+    }
+    
+    private boolean setLatticeValueTo(int reg, int value) {
+        return setLatticeValueTo(reg, value, null);
+    }
+    /**
+     * Simulates a PHI node and set the lattice for the result
+     * to the approriate value.
+     * Meet values:
+     * TOP x anything = anything
+     * VARYING x anything = VARYING
+     * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
+     * @param insn PHI to simulate.
+     */
+    private void simulatePhi(PhiInsn insn) {
+        int phiResultReg = insn.getResult().getReg();
+
+        if (latticeValues[phiResultReg] == VARYING) {
+            return;
+        }
+
+        RegisterSpecList sources = insn.getSources();
+        int phiResultValue = TOP;
+        Constant phiConstant = null;
+        int sourceSize = sources.size();
+        for (int i = 0; i < sourceSize; i++) {
+            int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
+            int sourceReg = sources.get(i).getReg();
+            int sourceRegValue = latticeValues[sourceReg];
+
+            if (!executableBlocks.get(predBlockIndex)
+                    || sourceRegValue == TOP) {
+                continue;
+            }
+
+            if (sourceRegValue == CONSTANT) {
+                if (phiConstant == null) {
+                    phiConstant = latticeConstants[sourceReg];
+                    phiResultValue = CONSTANT;
+                 } else if (!latticeConstants[sourceReg].equals(phiConstant)){
+                    phiResultValue = VARYING;
+                    break;
+                }
+
+            } else if (sourceRegValue == VARYING) {
+                phiResultValue = VARYING;
+                break;
+            }
+        }
+        if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
+            addUsersToWorklist(phiResultReg, phiResultValue);
+        }
+    }
+
+    /**
+     * Simulate a block and note the results in the lattice.
+     * @param block Block to visit
+     */
+    private void simulateBlock(SsaBasicBlock block) {
+        for (SsaInsn insn: block.getInsns()) {
+            if (insn instanceof PhiInsn) {
+                simulatePhi((PhiInsn) insn);
+            } else {
+                simulateStmt(insn);
+            }
+        }
+    }
+    private static String latticeValName(int latticeVal) {
+        switch (latticeVal) {
+            case TOP: return "TOP";
+            case CONSTANT: return "CONSTANT";
+            case VARYING: return "VARYING";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Simplifies a jump statement.
+     * @param insn jump to simplify
+     * @return an instruction representing the simplified jump.
+     */
+    private Insn simplifyJump (Insn insn) {
+        return insn;
+    }
+
+    /**
+     * Simulates math insns, if possible.
+     *
+     * @param insn non-null insn to simulate
+     * @return constant result or null if not simulatable.
+     */
+    private Constant simulateMath(SsaInsn insn) {
+        Insn ropInsn = insn.getOriginalRopInsn();
+        int opcode = insn.getOpcode().getOpcode();
+        RegisterSpecList sources = insn.getSources();
+        int regA = sources.get(0).getReg();
+        Constant cA;
+        Constant cB;
+
+        if (latticeValues[regA] != CONSTANT) {
+            cA = null;
+        } else {
+            cA = latticeConstants[regA];
+        }
+
+        if (sources.size() == 1) {
+            CstInsn cstInsn = (CstInsn) ropInsn;
+            cB = cstInsn.getConstant();
+        } else { /* sources.size() == 2 */
+            int regB = sources.get(1).getReg();
+            if (latticeValues[regB] != CONSTANT) {
+                cB = null;
+            } else {
+                cB = latticeConstants[regB];
+            }
+        }
+
+        if (cA == null || cB == null) {
+            //TODO handle a constant of 0 with MUL or AND
+            return null;
+        }
+
+        switch (insn.getResult().getBasicType()) {
+            case Type.BT_INT:
+                int vR;
+                boolean skip=false;
+
+                int vA = ((CstInteger) cA).getValue();
+                int vB = ((CstInteger) cB).getValue();
+
+                switch (opcode) {
+                    case RegOps.ADD:
+                        vR = vA + vB;
+                        break;
+                    case RegOps.SUB:
+                        vR = vA - vB;
+                        break;
+                    case RegOps.MUL:
+                        vR = vA * vB;
+                        break;
+                    case RegOps.DIV:
+                        if (vB == 0) {
+                            skip = true;
+                            vR = 0; // just to hide a warning
+                        } else {
+                            vR = vA / vB;
+                        }
+                        break;
+                    case RegOps.AND:
+                        vR = vA & vB;
+                        break;
+                    case RegOps.OR:
+                        vR = vA | vB;
+                        break;
+                    case RegOps.XOR:
+                        vR = vA ^ vB;
+                        break;
+                    case RegOps.SHL:
+                        vR = vA << vB;
+                        break;
+                    case RegOps.SHR:
+                        vR = vA >> vB;
+                        break;
+                    case RegOps.USHR:
+                        vR = vA >>> vB;
+                        break;
+                    case RegOps.REM:
+                        vR = vA % vB;
+                        break;
+                    default:
+                        throw new RuntimeException("Unexpected op");
+                }
+
+                return skip ? null : CstInteger.make(vR);
+
+            default:
+                // not yet supported
+                return null;
+        }
+    }
+
+    /**
+     * Simulates a statement and set the result lattice value.
+     * @param insn instruction to simulate
+     */
+    private void simulateStmt(SsaInsn insn) {
+        Insn ropInsn = insn.getOriginalRopInsn();
+        if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
+                || ropInsn.getOpcode().isCallLike()) {
+            ropInsn = simplifyJump (ropInsn);
+            /* TODO: If jump becomes constant, only take true edge. */
+            SsaBasicBlock block = insn.getBlock();
+            int successorSize = block.getSuccessorList().size();
+            for (int i = 0; i < successorSize; i++) {
+                int successor = block.getSuccessorList().get(i);
+                addBlockToWorklist(ssaMeth.getBlocks().get(successor));
+            }
+        }
+
+        if (insn.getResult() == null) {
+            return;
+        }
+
+        /* TODO: Simplify statements when possible using the constants. */
+        int resultReg = insn.getResult().getReg();
+        int resultValue = VARYING;
+        Constant resultConstant = null;
+        int opcode = insn.getOpcode().getOpcode();
+        switch (opcode) {
+            case RegOps.CONST: {
+                CstInsn cstInsn = (CstInsn)ropInsn;
+                resultValue = CONSTANT;
+                resultConstant = cstInsn.getConstant();
+                break;
+            }
+            case RegOps.MOVE: {
+                if (insn.getSources().size() == 1) {
+                    int sourceReg = insn.getSources().get(0).getReg();
+                    resultValue = latticeValues[sourceReg];
+                    resultConstant = latticeConstants[sourceReg];
+                }
+                break;
+            }
+
+            case RegOps.ADD:
+            case RegOps.SUB:
+            case RegOps.MUL:
+            case RegOps.DIV:
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+            case RegOps.SHL:
+            case RegOps.SHR:
+            case RegOps.USHR:
+            case RegOps.REM:
+
+                resultConstant = simulateMath(insn);
+
+                if (resultConstant == null) {
+                    resultValue = VARYING;
+                } else {
+                    resultValue = CONSTANT;
+                }
+            break;
+            /* TODO: Handle non-int arithmetic.
+               TODO: Eliminate check casts that we can prove the type of. */
+            default: {}
+        }
+        if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
+            addUsersToWorklist(resultReg, resultValue);
+        }
+    }
+
+    private void run() {
+        SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
+        addBlockToWorklist(firstBlock);
+
+        /* Empty all the worklists by propagating our values */
+        while (!cfgWorklist.isEmpty()
+                || !ssaWorklist.isEmpty()
+                || !varyingWorklist.isEmpty()) {
+            while (!cfgWorklist.isEmpty()) {
+                int listSize = cfgWorklist.size() - 1;
+                SsaBasicBlock block = cfgWorklist.remove(listSize);
+                simulateBlock(block);
+            }
+            while (!varyingWorklist.isEmpty()) {
+                int listSize = varyingWorklist.size() - 1;
+                SsaInsn insn = varyingWorklist.remove(listSize);
+
+                if (!executableBlocks.get(insn.getBlock().getIndex())) {
+                    continue;
+                }
+
+                if (insn instanceof PhiInsn) {
+                    simulatePhi((PhiInsn)insn);
+                } else {
+                    simulateStmt(insn);
+                }
+            }
+            while (!ssaWorklist.isEmpty()) {
+                int listSize = ssaWorklist.size() - 1;
+                SsaInsn insn = ssaWorklist.remove(listSize);
+
+                if (!executableBlocks.get(insn.getBlock().getIndex())) {
+                    continue;
+                }
+
+                if (insn instanceof PhiInsn) {
+                    simulatePhi((PhiInsn)insn);
+                } else {
+                    simulateStmt(insn);
+                }
+            }
+        }
+
+        replaceConstants();
+    }
+
+    /**
+     * Replaces TypeBearers in source register specs with constant type
+     * bearers if possible. These are then referenced in later optimization
+     * steps.
+     */
+    private void replaceConstants() {
+        for (int reg = 0; reg < regCount; reg++) {            
+            if (latticeValues[reg] != CONSTANT) {
+                continue;
+            }
+            if (!(latticeConstants[reg] instanceof TypedConstant)) {
+                // We can't do much with these
+                continue;
+            }
+
+            SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
+            TypeBearer typeBearer = defn.getResult().getTypeBearer();
+
+            if (typeBearer.isConstant()) {
+                /*
+                 * The definition was a constant already.
+                 * The uses should be as well.
+                 */
+                continue;
+            }
+
+            /*
+             * Update the sources RegisterSpec's of all non-move uses.
+             * These will be used in later steps.
+             */
+            for(SsaInsn insn: ssaMeth.getUseListForRegister(reg)) {
+                if (insn.isPhiOrMove()) {
+                    continue;
+                }
+
+                NormalSsaInsn nInsn = (NormalSsaInsn) insn;
+                RegisterSpecList sources = insn.getSources();
+
+                int index = sources.indexOfRegister(reg);
+
+                RegisterSpec spec = sources.get(index);
+                RegisterSpec newSpec
+                        = spec.withType((TypedConstant)latticeConstants[reg]);
+
+
+                nInsn.changeOneSource(index, newSpec);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SetFactory.java b/dx/src/com/android/dx/ssa/SetFactory.java
new file mode 100644
index 0000000..f34d08d
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SetFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.ListIntSet;
+
+
+/**
+ * Makes int sets for various parts of the optimizer.
+ */
+public final class SetFactory {
+
+    /**
+     * BitIntSet/ListIntSet threshold for dominance frontier sets. These
+     * sets are kept per basic block until phi placement and tend to be,
+     * like the CFG itself, very sparse at large sizes.
+     *
+     * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+     */
+    private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072;
+
+    /**
+     * BitIntSet/ListIntSet threshold for interference graph sets. These
+     * sets are kept per register until register allocation is done.
+     *
+     * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+     */
+    private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072;
+
+    /**
+     * BitIntSet/ListIntSet threshold for the live in/out sets kept by
+     * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic
+     * block during register allocation.
+     *
+     * The total size of a bitset for this would be the count of blocks
+     * times the size of registers. The threshold value here is merely
+     * the register count, which is typically on the order of the block
+     * count as well.
+     */
+    private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072;
+
+
+    /**
+     * Make IntSet for the dominance-frontier sets.
+     *
+     * @param szBlocks &gt;=0; count of basic blocks in method
+     * @return non-null; appropriate set
+     */
+    /*package*/ static IntSet makeDomFrontSet(int szBlocks) {
+        return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE
+                ? new BitIntSet(szBlocks)
+                : new ListIntSet();
+    }
+
+    /**
+     * Make IntSet for the interference graph sets. Public because
+     * InterferenceGraph is in another package.
+     *
+     * @param countRegs &gt;=0; count of SSA registers used in method
+     * @return non-null; appropriate set
+     */
+    public static IntSet makeInterferenceSet(int countRegs) {
+        return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE
+                ? new BitIntSet(countRegs)
+                : new ListIntSet();
+    }
+
+    /**
+     * Make IntSet for register live in/out sets.
+     *
+     * @param countRegs &gt;=0; count of SSA registers used in method
+     * @return non-null; appropriate set
+     */
+    /*package*/ static IntSet makeLivenessSet(int countRegs) {
+        return countRegs <= LIVENESS_SET_THRESHOLD_SIZE
+                ? new BitIntSet(countRegs)
+                : new ListIntSet();
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
new file mode 100644
index 0000000..99ada7e
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
@@ -0,0 +1,949 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.util.IntList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An SSA representation of a basic block.
+ */
+public final class SsaBasicBlock {
+
+    /** non-null; insn list associated with this instance */
+    private ArrayList<SsaInsn> insns;
+    /** non-null; predecessor set (by block list index) */
+    private BitSet predecessors;
+    /** non-null; successor set (by block list index) */
+    private BitSet successors;
+    /**
+     * non-null; ordered successor list
+     * (same block may be listed more than once)
+     */
+    private IntList successorList;
+    /** block list index of primary successor, or -1 for no primary successor */
+    private int primarySuccessor = -1;
+    /** label of block in rop form */
+    private int ropLabel;
+    /** non-null; method we belong to */
+    private SsaMethod parent;
+    /** our index into parent.getBlock() */
+    private int index;
+    /** list of dom children */
+    private final ArrayList<SsaBasicBlock> domChildren;
+
+    /** 
+     * The number of moves added to the end of the block during the
+     * phi-removal process. Retained for subsequent move scheduling.
+     */
+    private int movesFromPhisAtEnd = 0;
+    /** 
+     * The number of moves added to the beginning of the block during the
+     * phi-removal process. Retained for subsequent move scheduling.
+     */
+    private int movesFromPhisAtBeginning = 0;
+
+    /** null-ok; indexed by reg: the regs that are live-in at this block */
+    private IntSet liveIn;
+    /** null-ok; indexed by reg: the regs that are live-out at this block */
+    private IntSet liveOut;
+
+    /**
+     * Create a new empty basic block
+     * @param basicBlockIndex index this block will have
+     * @param ropLabel original rop-form label
+     * @param parent method of this block
+     */
+    public SsaBasicBlock(final int basicBlockIndex, final int ropLabel,
+            final SsaMethod parent) {
+        this.parent = parent;
+        this.index = basicBlockIndex;
+        this.insns = new ArrayList<SsaInsn>();
+        this.ropLabel = ropLabel;
+
+        this.predecessors = new BitSet(parent.getBlocks().size());
+        this.successors = new BitSet(parent.getBlocks().size());
+        this.successorList = new IntList();
+
+        domChildren = new ArrayList<SsaBasicBlock>();
+    }
+
+    /**
+     * Creates a new SSA basic block from a ROP form basic block.
+     *
+     * @param rmeth original method
+     * @param basicBlockIndex index this block will have
+     * @param parent method of this block
+     * predecessor set will be updated.
+     * @return new instance
+     */
+    public static SsaBasicBlock newFromRop(RopMethod rmeth,
+            int basicBlockIndex, final SsaMethod parent) {
+
+        BasicBlockList ropBlocks;
+        SsaBasicBlock result;
+        InsnList ropInsns;
+        BasicBlock bb;
+
+        ropBlocks = rmeth.getBlocks();
+        bb = ropBlocks.get(basicBlockIndex);
+
+        result = new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent);
+
+        ropInsns = bb.getInsns();
+
+        result.insns.ensureCapacity(ropInsns.size());
+        for (int i = 0, sz = ropInsns.size() ; i < sz ; i++) {
+            result.insns.add(new NormalSsaInsn (ropInsns.get(i), result));
+        }
+
+        result.predecessors = SsaMethod.bitSetFromLabelList(
+                ropBlocks,
+                rmeth.labelToPredecessors(bb.getLabel()));
+
+        result.successors
+                = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors());
+
+        result.successorList
+                = SsaMethod.indexListFromLabelList(ropBlocks,
+                    bb.getSuccessors());
+
+
+        if (result.successorList.size() != 0) {
+            int primarySuccessor = bb.getPrimarySuccessor();
+
+            result.primarySuccessor = (primarySuccessor < 0)
+                    ? -1 : ropBlocks.indexOfLabel(primarySuccessor);
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds a basic block as a dom child for this block. Used when constructing
+     * the dom tree.
+     *
+     * @param child non-null; new dom child
+     */
+    void addDomChild(SsaBasicBlock child) {
+        domChildren.add(child);
+    }
+
+    /**
+     * Gets the dom children for this node. Don't modify this list.
+     *
+     * @return non-null; list of dom children
+     */
+    ArrayList<SsaBasicBlock> getDomChildren() {
+        return domChildren;
+    }
+
+    /**
+     * Adds a phi insn to the beginning of this block. The result type of
+     * the phi will be set to void, to indicate that it's currently unknown.
+     *
+     * @param reg &gt;=0 result reg
+     */
+    void addPhiInsnForReg(int reg) {
+        insns.add(0, new PhiInsn(reg, this));
+    }
+
+    /**
+     * Adds a phi insn to the beginning of this block. This is to be used
+     * when the result type or local-association can be determined at phi
+     * insert time.
+     *
+     * @param resultSpec non-null; reg
+     */
+    void addPhiInsnForReg(RegisterSpec resultSpec) {
+        insns.add(0, new PhiInsn(resultSpec, this));
+    }
+
+    /**
+     * Adds an insn to the head of this basic block, just after any phi
+     * insns.
+     *
+     * @param insn non-null; rop-form insn to add
+     */
+    void addInsnToHead(Insn insn) {
+        SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+        insns.add(getCountPhiInsns(), newInsn);
+        parent.onInsnAdded(newInsn);
+    }
+
+    /**
+     * Replaces the last insn in this block. The provided insn must have
+     * some branchingness.
+     *
+     * @param insn non-null; rop-form insn to add, which must branch.
+     */
+    void replaceLastInsn(Insn insn) {
+        if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("last insn must branch");
+        }
+
+        SsaInsn oldInsn = insns.get(insns.size() - 1);
+        SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+
+        insns.set(insns.size() - 1, newInsn);
+
+        parent.onInsnRemoved(oldInsn);
+        parent.onInsnAdded(newInsn);
+    }
+
+    /**
+     * Visits each phi insn
+     * @param v callback
+     */
+    public void forEachPhiInsn(PhiInsn.Visitor v) {
+
+        int sz = insns.size();
+        for (int i = 0; i < sz; i++) {
+            SsaInsn insn = insns.get(i);
+            if (insn instanceof PhiInsn) {
+                v.visitPhiInsn((PhiInsn) insn);
+            } else {
+                /*
+                 * Presently we assume PhiInsn's are in a continuous
+                 * block at the top of the list
+                 */
+                break;
+            }
+        }
+    }
+
+    /**
+     * Deletes all phi insns. Do this after adding appropriate move insns.
+     */
+    public void removeAllPhiInsns() {
+        /*
+         * Presently we assume PhiInsn's are in a continuous
+         * block at the top of the list
+         */
+
+        insns.subList(0, getCountPhiInsns()).clear();
+    }
+
+    /**
+     * Gets the number of phi insns at the top of this basic block.
+     * @return count of phi insns
+     */
+    private int getCountPhiInsns() {
+        int countPhiInsns;
+
+        int sz = insns.size();
+        for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) {
+            SsaInsn insn = insns.get(countPhiInsns);
+            if (!(insn instanceof PhiInsn)) {
+                break;
+            }
+        }
+
+        return countPhiInsns;
+    }
+
+    /**
+     * @return non-null;the (mutable) instruction list for this block,
+     * with phi insns at the beginning.
+     */
+    public ArrayList<SsaInsn> getInsns() {
+        return insns;
+    }
+
+    /**
+     * @return non-null; the (mutable) list of phi insns for this block
+     */
+    public List<SsaInsn> getPhiInsns() {
+        return insns.subList(0, getCountPhiInsns());
+    }
+
+    /**
+     * @return the block index of this block
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * @return the label of this block in rop form
+     */
+    public int getRopLabel() {
+        return ropLabel;
+    }
+
+    /**
+     * @return the label of this block in rop form as a hex string
+     */
+    public String getRopLabelString() {
+        return Hex.u2(ropLabel);
+    }
+
+    /**
+     * @return non-null;predecessors set, indexed by block index
+     */
+    public BitSet getPredecessors() {
+        return predecessors;
+    }
+
+    /**
+     * @return non-null;successors set, indexed by block index
+     */
+    public BitSet getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * @return non-null;ordered successor list, containing block indicies
+     */
+    public IntList getSuccessorList() {
+        return successorList;
+    }
+
+    /**
+     * @return &gt;= -1; block index of primary successor or -1 if no
+     * primary successor.
+     */
+    public int getPrimarySuccessorIndex() {
+        return primarySuccessor;
+    }
+
+    /**
+     * @return rop label of primary successor
+     */
+    public int getPrimarySuccessorRopLabel() {
+        return parent.blockIndexToRopLabel(primarySuccessor);
+    }
+
+    /**
+     * @return null-ok; the primary successor block or null if there is none.
+     */
+    public SsaBasicBlock getPrimarySuccessor() {
+        if (primarySuccessor < 0) {
+            return null;
+        } else {
+            return parent.getBlocks().get(primarySuccessor);
+        }
+    }
+
+    /**
+     * @return successor list of rop labels
+     */
+    public IntList getRopLabelSuccessorList() {
+        IntList result = new IntList(successorList.size());
+
+        int sz = successorList.size();
+
+        for (int i = 0; i < sz; i++) {
+            result.add(parent.blockIndexToRopLabel(successorList.get(i)));
+        }
+        return result;
+    }
+
+    /**
+     * @return non-null; method that contains this block
+     */
+    public SsaMethod getParent() {
+        return parent;
+    }
+
+    /**
+     * Inserts a new empty GOTO block as a predecessor to this block.
+     * All previous predecessors will be predecessors to the new block.
+     *
+     * @return non-null; an appropriately-constructed instance
+     */
+    public SsaBasicBlock insertNewPredecessor() {
+        SsaBasicBlock newPred = parent.makeNewGotoBlock();
+
+        // Update the new block
+        newPred.predecessors = predecessors;
+        newPred.successors.set(index) ;
+        newPred.successorList.add(index);
+        newPred.primarySuccessor = index;
+
+
+        // Update us
+        predecessors = new BitSet(parent.getBlocks().size());
+        predecessors.set(newPred.index);
+
+        // Update our (soon-to-be) old predecessors
+        for (int i = newPred.predecessors.nextSetBit(0); i >= 0;
+                i = newPred.predecessors.nextSetBit(i + 1)) {
+
+            SsaBasicBlock predBlock = parent.getBlocks().get(i);
+
+            predBlock.replaceSuccessor(index, newPred.index);
+        }
+
+        return newPred;
+    }
+
+    /**
+     * Constructs and inserts a new empty GOTO block <code>Z</code> between
+     * this block (<code>A</code>) and a current successor block
+     * (<code>B</code>). The new block will replace B as A's successor and
+     * A as B's predecessor. A and B will no longer be directly connected.
+     * If B is listed as a successor multiple times, all references
+     * are replaced.
+     *
+     * @param other current successor (B)
+     * @return non-null; an appropriately-constructed instance
+     */
+    public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) {
+        SsaBasicBlock newSucc = parent.makeNewGotoBlock();
+
+        if (!successors.get(other.index)) {
+            throw new RuntimeException("Block " + other.getRopLabelString()
+                    + " not successor of " + getRopLabelString());
+        }
+
+        // Update the new block
+        newSucc.predecessors.set(this.index);
+        newSucc.successors.set(other.index) ;
+        newSucc.successorList.add(other.index);
+        newSucc.primarySuccessor = other.index;
+
+        // Update us
+        for (int i = successorList.size() - 1 ;  i >= 0; i--) {
+            if(successorList.get(i) == other.index) {
+                successorList.set(i, newSucc.index);
+            }
+        }
+
+        if (primarySuccessor == other.index) {
+            primarySuccessor = newSucc.index;
+        }
+        successors.clear(other.index);
+        successors.set(newSucc.index);
+
+        // Update "other"
+        other.predecessors.set(newSucc.index);
+        other.predecessors.set(index, successors.get(other.index));
+
+        return newSucc;
+    }
+
+    /**
+     * Replace an old successor with a new successor.
+     * Throws RuntimeException if oldIndex was not a successor.
+     * @param oldIndex index of old successor block
+     * @param newIndex index of new successor block.
+     */
+    public void replaceSuccessor(int oldIndex, int newIndex) {
+        if (oldIndex == newIndex) {
+            return;
+        }
+
+        // Update us
+        successors.set(newIndex);
+
+        if (primarySuccessor == oldIndex) {
+            primarySuccessor = newIndex;
+        }
+
+        for (int i = successorList.size() - 1 ;  i >= 0; i--) {
+            if(successorList.get(i) == oldIndex) {
+                successorList.set(i, newIndex);
+            }
+        }
+
+        successors.clear(oldIndex);
+
+        // Update new successor
+        parent.getBlocks().get(newIndex).predecessors.set(index);
+
+        // Update old successor
+        parent.getBlocks().get(oldIndex).predecessors.clear(index);
+    }
+
+
+    /**
+     * Attaches block to an exit block if necessary. If this block
+     * is not an exit predecessor or is the exit block, this block does
+     * nothing. For use by {@link com.android.dx.ssa.SsaMethod#makeExitBlock}
+     *
+     * @param exitBlock non-null; exit block
+     */
+    public void exitBlockFixup(SsaBasicBlock exitBlock) {
+        if (this == exitBlock) {
+            return;
+        }
+
+        if (successorList.size() == 0) {
+            /*
+             * This is an exit predecessor.
+             * Set the successor to the exit block
+             */
+            successors.set(exitBlock.index);
+            successorList.add(exitBlock.index);
+            primarySuccessor = exitBlock.index;
+            exitBlock.predecessors.set(this.index);
+        }
+    }
+
+    /**
+     * Adds a move instruction to the end of this basic block, just
+     * before the last instruction. If the result of the final instruction
+     * is the source in question, then the move is placed at the beginning of
+     * the primary successor block. This is for unversioned registers.
+     * @param result move destination
+     * @param source move source
+     */
+    public void addMoveToEnd(RegisterSpec result, RegisterSpec source) {
+
+        if (result.getReg() == source.getReg()) {
+            // Sometimes we end up with no-op moves. Ignore them here.
+            return;
+        }
+
+        /*
+         * The last Insn has to be a normal SSA insn: a phi can't branch
+         * or return or cause an exception, etc.
+         */
+        NormalSsaInsn lastInsn;
+        lastInsn = (NormalSsaInsn)insns.get(insns.size()-1);
+
+        if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) {
+            /*
+             * The final insn in this block has a source or result register,
+             * and the moves we may need to place and schedule may interfere.
+             * We need to insert this instruction at the
+             * beginning of the primary successor block instead. We know
+             * this is safe, because when we edge-split earlier, we ensured
+             * that each successor has only us as a predecessor.
+             */
+
+            for (int i = successors.nextSetBit(0)
+                    ; i >= 0
+                    ; i = successors.nextSetBit(i + 1)) {
+
+                SsaBasicBlock succ;
+
+                succ = parent.getBlocks().get(i);
+                succ.addMoveToBeginning(result, source);
+            }
+        } else {
+            /*
+             * We can safely add a move to the end of the block
+             * just before the last instruction because
+             * the final insn does not assign to anything.
+             */
+
+            RegisterSpecList sources;
+            sources = RegisterSpecList.make(source);
+
+            NormalSsaInsn toAdd;
+
+            toAdd = new NormalSsaInsn(
+                        new PlainInsn(Rops.opMove(result.getType()),
+                                SourcePosition.NO_INFO, result, sources), this);
+
+            insns.add(insns.size() - 1, toAdd);
+
+            movesFromPhisAtEnd++;
+        }
+    }
+
+    /**
+     * Add a move instruction after the phi insn block.
+     * @param result move destination
+     * @param source move source
+     */
+    public void addMoveToBeginning (RegisterSpec result, RegisterSpec source) {
+               
+        if (result.getReg() == source.getReg()) {
+            // Sometimes we end up with no-op moves. Ignore them here.
+            return;
+        }
+
+        RegisterSpecList sources;
+        sources = RegisterSpecList.make(source);
+
+        NormalSsaInsn toAdd;
+
+        toAdd = new NormalSsaInsn(
+                    new PlainInsn(Rops.opMove(result.getType()),
+                            SourcePosition.NO_INFO, result, sources), this);
+
+        insns.add(getCountPhiInsns(), toAdd);
+        movesFromPhisAtBeginning++;        
+    }
+
+    /**
+     * Sets the register as used in a bitset, taking into account its
+     * category/width.
+     *
+     * @param regsUsed set, indexed by register number
+     * @param rs register to mark as used
+     */
+    private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) {
+        regsUsed.set(rs.getReg());
+        if (rs.getCategory() > 1) {
+            regsUsed.set(rs.getReg() + 1);            
+        }
+    }
+
+    /**
+     * Checks to see if the register is used in a bitset, taking
+     * into account its category/width.
+     *
+     * @param regsUsed set, indexed by register number
+     * @param rs register to mark as used
+     * @return true if register is fully or partially (for the case of wide
+     * registers) used.
+     */
+    private static boolean checkRegUsed (BitSet regsUsed, RegisterSpec rs) {
+        int reg = rs.getReg();
+        int category = rs.getCategory();
+
+        return regsUsed.get(reg)
+                || (category == 2 ? regsUsed.get(reg + 1) : false);
+    }
+
+    /**
+     * Ensures that all move operations in this block occur such that
+     * reads of any register happen before writes to that register.
+     * NOTE: caller is expected to returnSpareRegisters()!
+     *
+     * TODO See Briggs, et al "Practical Improvements to the Construction and
+     * Destruction of Static Single Assignment Form" section 5. a) This can
+     * be done in three passes.
+     * @param toSchedule List of instructions. Must consist only of moves.
+     */
+    private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) {
+        BitSet regsUsedAsSources = new BitSet(parent.getRegCount());
+        // TODO get rid of this
+        BitSet regsUsedAsResults = new BitSet(parent.getRegCount());
+
+        int sz = toSchedule.size();
+
+        int insertPlace = 0;
+
+        while (insertPlace < sz) {
+            int oldInsertPlace = insertPlace;
+
+            // Record all registers used as sources in this block.
+            for (int i = insertPlace; i < sz; i++) {
+                setRegsUsed(regsUsedAsSources,
+                        toSchedule.get(i).getSources().get(0));
+
+                setRegsUsed(regsUsedAsResults,
+                        toSchedule.get(i).getResult());
+            }
+
+            /*
+             * If there are no circular dependencies, then there exists
+             * n instructions where n > 1 whose result is not used as a source.
+             */
+            for (int i = insertPlace; i <sz; i++) {
+                SsaInsn insn = toSchedule.get(i);
+
+                /*
+                 * Move these n registers to the front, since they overwrite
+                 * nothing.
+                 */
+                if (!checkRegUsed(regsUsedAsSources, insn.getResult())) {
+                    Collections.swap(toSchedule, i, insertPlace++);
+                }
+            }
+
+            // If we've made no progress in this iteration, there's a
+            // circular dependency.  Split it using the temp reg.
+            if (oldInsertPlace == insertPlace) {
+
+                SsaInsn insnToSplit = null;
+
+                // Find an insn whose result is used as a source.
+                for (int i = insertPlace; i < sz; i++) {
+                    SsaInsn insn = toSchedule.get(i);
+                    if (checkRegUsed(regsUsedAsSources, insn.getResult())
+                            && checkRegUsed(regsUsedAsResults,
+                                insn.getSources().get(0))) {
+
+                        insnToSplit = insn;
+                        // We're going to split this insn--move it to the
+                        // front
+                        Collections.swap(toSchedule, insertPlace, i);
+                        break;
+                    }
+                }
+
+                // At least one insn will be set above
+
+                RegisterSpec result = insnToSplit.getResult();
+                RegisterSpec tempSpec = result.withReg(
+                        parent.borrowSpareRegister(result.getCategory()));
+
+                NormalSsaInsn toAdd;
+
+                toAdd = new NormalSsaInsn(
+                            new PlainInsn(Rops.opMove(result.getType()),
+                                    SourcePosition.NO_INFO,
+                                    tempSpec,
+                                    insnToSplit.getSources()), this);
+
+                toSchedule.add(insertPlace++, toAdd);
+
+                NormalSsaInsn toReplace;
+                RegisterSpecList newSources;
+
+                newSources = RegisterSpecList.make(tempSpec);
+
+                toReplace = new NormalSsaInsn(
+                            new PlainInsn(Rops.opMove(result.getType()),
+                                    SourcePosition.NO_INFO,
+                                    result,
+                                    newSources), this);
+
+                toSchedule.set(insertPlace, toReplace);
+
+                // size has changed
+                sz = toSchedule.size();
+            }
+
+            regsUsedAsSources.clear();
+            regsUsedAsResults.clear();
+        }
+    }
+
+    /**
+     * Adds regV to the live-out list for this block.
+     * Called by the liveness analyzer.
+     * @param regV register that is live-out for this block.
+     */
+    public void
+    addLiveOut (int regV) {
+        if (liveOut == null) {
+            liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+
+        liveOut.add(regV);
+    }
+
+    /**
+     * Adds regV to the live-in list for this block.
+     * Called by the liveness analyzer.
+     * @param regV register that is live-in for this block.
+     */
+    public void
+    addLiveIn (int regV) {
+        if (liveIn == null) {
+            liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+
+        liveIn.add(regV);       
+    }
+
+    /**
+     * Returns the set of live-in registers. Valid after register
+     * interference graph has been generated, otherwise empty.
+     *
+     * @return non-null; live-in register set.
+     */
+    public IntSet getLiveInRegs() {
+        if (liveIn == null) {
+            liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+        return liveIn;
+    }
+
+    /**
+     * Returns the set of live-out registers. Valid after register
+     * interference graph has been generated, otherwise empty.
+     * 
+     * @return non-null; live-out register set.
+     */
+    public IntSet getLiveOutRegs() {
+        if (liveOut == null) {
+            liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+        return liveOut;
+    }
+
+    /**
+     * @return true if this is the one-and-only exit block for this method
+     */
+    public boolean isExitBlock() {
+        return index == parent.getExitBlockIndex();
+    }
+
+    /**
+     * Returns true if this block is reachable (that is, it hasn't been
+     * unlinked from the control flow of this method). This currently tests
+     * that it's either the start block or it has predecessors, which suffices
+     * for all current control flow transformations.
+     *
+     * @return true if reachable
+     */
+    public boolean isReachable() {
+        return index == parent.getEntryBlockIndex()
+                || predecessors.cardinality() > 0;
+    }
+        
+    /**
+     * Sorts move instructions added via <code>addMoveToEnd</code> during
+     * phi removal so that results don't overwrite sources that are used.
+     * For use after all phis have been removed and all calls to
+     * addMoveToEnd() have been made.<p>
+     *
+     * This is necessary because copy-propogation may have left us in a state
+     * where the same basic block has the same register as a phi operand
+     * and a result. In this case, the register in the phi operand always
+     * refers value before any other phis have executed.
+     */
+    public void scheduleMovesFromPhis() {
+
+        if (movesFromPhisAtBeginning > 1) {
+            List<SsaInsn> toSchedule;
+
+            toSchedule = insns.subList(0, movesFromPhisAtBeginning);
+
+            scheduleUseBeforeAssigned(toSchedule);
+
+            SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning);
+
+            //TODO it's actually possible that this case never happens,
+            //because a move-exception block, having only one predecessor
+            //in SSA form, perhaps is never on a dominance frontier.
+            if (firstNonPhiMoveInsn.isMoveException()) {
+                if (true) {
+                    /*
+                     * We've yet to observe this case, and if it can
+                     * occur the code written to handle it probably 
+                     * does not work.
+                     */
+                    throw new RuntimeException(
+                            "Unexpected: moves from "
+                                    +"phis before move-exception");
+                } else {
+
+                    // A move-exception insn must be placed first in this block
+                    // We need to move it there, and deal with possible
+                    // interference.
+                    boolean moveExceptionInterferes = false;
+
+                    int moveExceptionResult
+                            = firstNonPhiMoveInsn.getResult().getReg();
+
+                    // Does the move-exception result reg interfere with the
+                    // phi moves?
+                    for(SsaInsn insn: toSchedule) {
+                        if (insn.isResultReg(moveExceptionResult)
+                                || insn.isRegASource(moveExceptionResult)) {
+                            moveExceptionInterferes = true;
+                            break;
+                        }
+                    }
+
+                    if (!moveExceptionInterferes) {
+                        // The easy case
+                        insns.remove(movesFromPhisAtBeginning);
+                        insns.add(0, firstNonPhiMoveInsn);
+                    } else {
+                        // We need to move the result to a spare reg and move it
+                        // back.
+
+                        int spareRegister;
+                        RegisterSpec originalResultSpec;
+
+                        originalResultSpec = firstNonPhiMoveInsn.getResult();
+                        spareRegister = parent.borrowSpareRegister(
+                                originalResultSpec.getCategory());
+
+                        // We now move it to a spare register
+                        firstNonPhiMoveInsn.changeResultReg(spareRegister);
+                        RegisterSpec tempSpec = firstNonPhiMoveInsn.getResult();
+
+                        insns.add(0, firstNonPhiMoveInsn);
+
+                        // And here we move it back
+
+                        NormalSsaInsn toAdd;
+
+                        toAdd = new NormalSsaInsn(
+                                    new PlainInsn(Rops.opMove(
+                                            tempSpec.getType()),
+                                            SourcePosition.NO_INFO,
+                                            originalResultSpec,
+                                            RegisterSpecList.make(tempSpec)),
+                                this);
+
+
+                        // Place it immediately after the phi-moves,
+                        // overwriting the move-exception that was there.
+                        insns.set(movesFromPhisAtBeginning + 1, toAdd);
+                    }
+                }
+            }
+        }
+        if (movesFromPhisAtEnd > 1) {
+            scheduleUseBeforeAssigned(
+                    insns.subList(insns.size() - movesFromPhisAtEnd - 1,
+                                insns.size() - 1));
+        }
+
+        // Return registers borrowed here and in scheduleUseBeforeAssigned()
+        parent.returnSpareRegisters();
+
+    }
+
+    /**
+     * Visit all insns in this block
+     * @param visitor callback interface
+     */
+    public void forEachInsn(SsaInsn.Visitor visitor) {
+        for (SsaInsn insn: insns) {
+            insn.accept(visitor);
+        }
+    }
+
+    public String toString() {
+        return "{" + index + ":" + Hex.u2(ropLabel) + '}';
+    }
+
+    /**
+     * Visitor interface for basic blocks
+     */
+    public interface Visitor {
+
+        /**
+         * Indicates a block has been visited by an iterator method.
+         * @param v non-null; block visited
+         * @param parent null-ok; parent node if applicable.
+         */
+        void visitBlock (SsaBasicBlock v, SsaBasicBlock parent);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaConverter.java b/dx/src/com/android/dx/ssa/SsaConverter.java
new file mode 100644
index 0000000..a731fcb
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaConverter.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * Converts ROP methods to SSA Methods
+ */
+public class SsaConverter {
+    public static boolean DEBUG = false;
+
+    /**
+     * returns an SSA representation, edge-split and with phi functions placed
+     * @param rmeth input
+     * @param paramWidth the total width, in register-units, of the method's
+     * parameters
+     * @param isStatic true if this method has no 'this'
+     * pointer argument
+     * @return output in SSA form
+     */
+    public static SsaMethod convertToSsaMethod(RopMethod rmeth, 
+            int paramWidth, boolean isStatic) {
+        SsaMethod result;
+
+        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+
+        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+        placePhiFunctions(result, localInfo);
+        new SsaRenamer(result).run();
+
+        /*
+         * Exit block, added here,  is not considered for edge splitting
+         * or phi placement since no actual control flows to it.
+         */
+        result.makeExitBlock();
+
+        return result;
+    }
+
+    /**
+     * Returns an SSA represention with only the edge-splitter run.
+     * @param rmeth method to process
+     * @param paramWidth width of all arguments in the method
+     * @param isStatic true if this method has no 'this' pointer argument
+     * @return an SSA represention with only the edge-splitter run.
+     */
+    public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
+            boolean isStatic) {
+        SsaMethod result;
+
+        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+        return result;
+    }
+
+    /**
+     * Returns an SSA represention with only the steps through the
+     * phi placement run.
+     * @param rmeth method to process
+     * @param paramWidth width of all arguments in the method
+     * @param isStatic true if this method has no 'this' pointer argument
+     * @return an SSA represention with only the edge-splitter run.
+     */
+    public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
+            boolean isStatic) {
+        SsaMethod result;
+
+        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+
+        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+        placePhiFunctions(result, localInfo);
+        return result;
+    }
+
+    /**
+     * See Appel section 19.1
+     * Converts CFG into "edge-split" form, such that each node either a
+     * unique successor or unique predecessor.<p>
+     *
+     * In addition, the SSA form we use enforces a further constraint,
+     * requiring each block with a final instruction that returns a
+     * value to have a primary successor that has no other
+     * predecessor. This ensures move statements can always be
+     * inserted correctly when phi statements are removed.
+     * 
+     * @param result method to process
+     */
+    private static void edgeSplit(SsaMethod result) {
+
+        edgeSplitPredecessors(result);
+        edgeSplitMoveExceptionsAndResults(result);
+        edgeSplitSuccessors(result);
+    }
+
+    /**
+     * Inserts Z nodes as new predecessors for every node that has multiple
+     * successors and multiple predecessors.
+     * @param result non-null; method to process
+     */
+    private static void edgeSplitPredecessors(SsaMethod result) {
+        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+        
+        // New blocks are added to the end of the block list during
+        // this iteration
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+            if (nodeNeedsUniquePredecessor(block)) {
+                block.insertNewPredecessor();
+            }
+        }
+    }
+
+    /**
+     * @param block non-null; block in question
+     * @return true if this node needs to have a unique predecessor created for
+     * it.
+     */
+    private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
+
+        /*
+         * Any block with that has both multiple successors and multiple
+         * predecessors needs a new predecessor node.
+         */
+
+        int countPredecessors = block.getPredecessors().cardinality();
+        int countSuccessors = block.getSuccessors().cardinality();
+
+        return  (countPredecessors > 1 && countSuccessors > 1);
+    }
+
+    /**
+     * In ROP form, move-exception must occur as the first insn in a block
+     * immediately succeeding the insn that could thrown an exception.
+     * We may need room to insert move insns later, so make sure to split
+     * any block that starts with a move-exception such that there is a
+     * unique move-exception block for each predecessor.
+     * @param ssaMeth method to process
+     */
+    private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        // New blocks are added to the end of the block list during
+        // this iteration
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+        
+            // Any block that starts with a move-exception and has more than
+            // one predecessor...
+            if (!block.isExitBlock()
+                    && block.getPredecessors().cardinality() > 1 
+                    && block.getInsns().get(0).isMoveException()) {
+
+                // block.getPredecessors() is changed in the loop below
+                BitSet preds = (BitSet)block.getPredecessors().clone();
+                for (int j = preds.nextSetBit(0); j >= 0;
+                        j = preds.nextSetBit(j + 1)) {
+
+                    SsaBasicBlock predecessor = blocks.get(j);
+
+                    SsaBasicBlock zNode = predecessor.insertNewSuccessor(block);
+
+                    // Make sure to place the move-exception as the
+                    // first insn...
+                    zNode.getInsns().add(0, block.getInsns().get(0).clone());
+                }
+
+                // remove the move-exception from the original block...
+                block.getInsns().remove(0);
+            }
+        }
+    }
+
+    /**
+     * Inserts Z nodes for every node that needs a new 
+     * successor.
+     * @param result non-null; method to process
+     */
+    private static void edgeSplitSuccessors(SsaMethod result) {
+        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+        // New blocks are added to the end of the block list during
+        // this iteration
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+
+            // successors list is modified in loop below
+            BitSet successors = (BitSet)block.getSuccessors().clone();
+            for(int j = successors.nextSetBit(0);
+                    j >= 0; j = successors.nextSetBit(j+1)) {
+
+                SsaBasicBlock succ = blocks.get(j);
+
+                if (needsNewSuccessor(block, succ)) {
+                    block.insertNewSuccessor(succ);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if block and successor need a Z-node between them.
+     * Presently, this is true if the final instruction has any sources
+     * or results and the current successor block has more than one
+     * predecessor.
+     * @param block predecessor node
+     * @param succ successor node
+     * @return true if a Z node is needed
+     */
+    private static boolean needsNewSuccessor(SsaBasicBlock block,
+            SsaBasicBlock succ) {
+
+        ArrayList<SsaInsn> insns = block.getInsns();
+        SsaInsn lastInsn = insns.get(insns.size() - 1);
+
+        return ((lastInsn.getResult() != null)
+                    || (lastInsn.getSources().size() > 0))
+                && succ.getPredecessors().cardinality() > 1;
+    }
+
+    /**
+     * See Appel algorithm 19.6
+     * Place Phi functions in appropriate locations.
+     *
+     * @param ssaMeth non-null; method to process. Modifications made in-place
+     * @param localInfo non-null; Local variable info, used when placing phis
+     */
+    private static void placePhiFunctions (SsaMethod ssaMeth,
+            LocalVariableInfo localInfo) {
+        ArrayList<SsaBasicBlock> ssaBlocks;
+        int regCount;
+        int blockCount;
+
+        ssaBlocks = ssaMeth.getBlocks();
+        blockCount = ssaBlocks.size();
+        regCount = ssaMeth.getRegCount();
+
+        DomFront df = new DomFront(ssaMeth);
+        DomFront.DomInfo[] domInfos = df.run();
+
+        // Bit set of registers vs block index "definition sites"
+        BitSet[] defsites = new BitSet[regCount];
+
+        // Bit set of registers vs block index "phi placement sites"
+        BitSet[] phisites = new BitSet[regCount];
+
+        for (int i = 0; i < regCount; i++) {
+            defsites[i] = new BitSet(blockCount);
+            phisites[i] = new BitSet(blockCount);
+        }
+
+        /*
+         * For each register, build a set of all basic blocks where
+         * containing an assignment to that register.
+         */
+        for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
+            SsaBasicBlock b = ssaBlocks.get(bi);
+
+            for (SsaInsn insn: b.getInsns()) {
+
+                RegisterSpec rs = insn.getResult();
+
+                if (rs != null) {
+                    defsites[rs.getReg()].set(bi);
+                }
+            }
+        }
+
+        if (DEBUG) {
+            System.out.println("defsites");
+
+            for (int i = 0; i < regCount; i++) {
+                StringBuilder sb = new StringBuilder();
+
+                sb.append('v').append(i).append(": ");
+
+                sb.append(defsites[i].toString());
+
+                System.out.println(sb);
+            }
+        }
+
+        BitSet worklist;
+
+        /*
+         * For each register, compute all locations for phi placement
+         * based on dominance-frontier algorithm.
+         */
+        for (int reg = 0, s = ssaMeth.getRegCount() ; reg < s ; reg++ ) {
+            int workBlockIndex;
+
+            /* Worklist set starts out with each node where reg is assigned */
+
+            worklist = (BitSet)(defsites[reg].clone());
+
+            while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
+                worklist.clear(workBlockIndex);
+                IntIterator dfIterator
+                        = domInfos[workBlockIndex]
+                        .dominanceFrontiers.iterator();
+
+                while (dfIterator.hasNext()) {
+                    int dfBlockIndex = dfIterator.next();
+
+                    if (!phisites[reg].get(dfBlockIndex)) {
+                        phisites[reg].set(dfBlockIndex);
+
+                        RegisterSpec rs
+                                = localInfo.getStarts(dfBlockIndex).get(reg);
+
+                        if (rs == null) {
+                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(reg);
+                        } else {
+                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
+                        }
+
+                        if (!defsites[reg].get(dfBlockIndex)) {
+                            worklist.set(dfBlockIndex);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (DEBUG) {
+            System.out.println("phisites");
+
+            for (int i = 0; i < regCount; i++) {
+                StringBuilder sb = new StringBuilder();
+
+                sb.append('v').append(i).append(": ");
+
+                sb.append(phisites[i].toString());
+
+                System.out.println(sb);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaInsn.java b/dx/src/com/android/dx/ssa/SsaInsn.java
new file mode 100644
index 0000000..d9e33a0
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaInsn.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.util.ToHuman;
+
+/**
+ * An instruction in SSA form
+ */
+public abstract class SsaInsn implements ToHuman, Cloneable {
+
+    protected RegisterSpec result;
+    protected final SsaBasicBlock block;
+
+    /**
+     * Constructs an instance
+     * @param block block containing this insn. Can never change.
+     */
+    protected SsaInsn(final SsaBasicBlock block) {
+        this.block = block;
+    }
+
+    /**
+     * Makes a new SSA insn form a ROP insn
+     *
+     * @param insn non-null; rop insn
+     * @param block non-null; owning block
+     * @return non-null; an appropriately constructed instance
+     */
+    public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) {
+        return new NormalSsaInsn(insn, block);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SsaInsn clone() {
+        try {
+            return (SsaInsn)super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new RuntimeException ("unexpected", ex);
+        }
+    }
+
+    /**
+     * Like {@link com.android.dx.rop.code.Insn getResult()}.
+     * @return result register
+     */
+    public RegisterSpec getResult() {
+        return result;
+    }
+
+    /**
+     * Like {@link com.android.dx.rop.code.Insn getSources()}.
+     * @return non-null; sources list
+     */
+    abstract public RegisterSpecList getSources();
+
+    /**
+     * Gets the block to which this insn instance belongs.
+     *
+     * @return owning block
+     */
+    public SsaBasicBlock getBlock() {
+        return block;
+    }
+
+    /**
+     * is the specified reg the result reg?
+     * @param reg register to test
+     * @return true if there is a result and it is stored in the specified
+     * register
+     */
+    public boolean isResultReg(int reg) {
+        return result != null && result.getReg() == reg;
+    }
+
+
+    /**
+     * Changes the result register if this insn has a result.
+     * Used during renaming.
+     * @param reg new result register.
+     */
+    public void changeResultReg(int reg) {
+        if (result != null) {
+            result = result.withReg(reg);
+        }
+    }
+
+    /**
+     * Sets the local association for the result of this insn.
+     * This is sometimes updated during the SsaRenamer process.
+     *
+     * @param local null-ok; New debug/local variable info.
+     */
+    public final void setResultLocal(LocalItem local) {
+        LocalItem oldItem = result.getLocalItem();
+
+        if (local != oldItem && (local == null
+                || !local.equals(result.getLocalItem()))) {
+            result = RegisterSpec.makeLocalOptional(
+                    result.getReg(), result.getType(), local);
+        }
+    }
+
+    /**
+     * Map registers after register allocation.
+     *
+     * @param mapper
+     */
+    public final void mapRegisters(RegisterMapper mapper) {
+        RegisterSpec oldResult = result;
+        result = mapper.map(result);
+        block.getParent().updateOneDefinition(this, oldResult);
+        mapSourceRegisters(mapper);        
+    }
+
+    /**
+     * Maps only source registers.
+     *
+     * @param mapper new mapping
+     */
+    abstract public void mapSourceRegisters(RegisterMapper mapper);
+
+
+    /**
+     * Returns the Rop opcode for this insn, or null if this is a phi insn
+     *
+     * TODO move this up into NormalSsaInsn
+     *
+     * @return null-ok; Rop opcode if there is one.
+     */
+    abstract public Rop getOpcode();
+
+    /**
+     * Returns the original Rop insn for this insn, or null if this is
+     * a phi insn.
+     * 
+     * TODO move this up into NormalSsaInsn
+     *
+     * @return null-ok; Rop insn if there is one.
+     */
+    abstract public Insn getOriginalRopInsn();
+
+    /**
+     * Gets the spec of a local variable assignment that occurs at this
+     * instruction, or null if no local variable assignment occurs. This
+     * may be the result register, or for <code>mark-local</code> insns
+     * it may be the source.
+     *
+     * @return null-ok; a local-associated register spec or null
+     * @see com.android.dx.rop.code.Insn#getLocalAssignment() 
+     */
+    public RegisterSpec getLocalAssignment() {
+        if (result != null && result.getLocalItem() != null) {
+            return result;
+        }
+
+        return null;
+    }
+
+    /**
+     * Indicates whether the specified register is amongst the registers
+     * used as sources for this instruction.
+     * @param reg The register in question
+     * @return true if the reg is a source
+     */
+    public boolean isRegASource(int reg) {
+        return null != getSources().specForRegister(reg);
+    }
+
+    /**
+     * Transform back to ROP form.
+     *
+     * TODO move this up into NormalSsaInsn
+     *
+     * @return non-null; a ROP representation of this instruction, with
+     * updated registers.
+     */
+    public abstract Insn toRopInsn();
+
+    /**
+     * @return true if this is a PhiInsn or a normal move insn
+     */
+    public abstract boolean isPhiOrMove();
+
+    /**
+     * Returns true if this insn is considered to have a side effect beyond
+     * that of assigning to the result reg.
+     * 
+     * @return true if this insn is considered to have a side effect beyond
+     * that of assigning to the result reg.
+     */
+    public abstract boolean hasSideEffect();
+
+    /**
+     * @return true if this is a move (but not a move-operand or move-exception)
+     * instruction
+     */
+    public boolean isNormalMoveInsn() {
+        return false;
+    }
+
+    /**
+     * @return true if this is a move-exception instruction.
+     * These instructions must immediately follow a preceeding invoke*
+     */
+    public boolean isMoveException() {
+        return false;
+    }
+
+    /**
+     * @return true if this instruction can throw.
+     */
+    abstract public boolean canThrow();
+
+    /**
+     * accepts a visitor
+     * @param v visitor
+     */
+    public abstract void accept(Visitor v);
+
+    /**
+     * Visitor interface for this class.
+     */
+    public static interface Visitor {
+
+        /**
+         * Any non-phi move instruction
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitMoveInsn(NormalSsaInsn insn);
+
+        /**
+         * Any phi insn
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitPhiInsn(PhiInsn insn);
+
+        /**
+         * Any insn that isn't a move or a phi (which is also a move).
+         * @param insn non-null; the instruction to visit
+         */
+        public void visitNonMoveInsn(NormalSsaInsn insn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaMethod.java b/dx/src/com/android/dx/ssa/SsaMethod.java
new file mode 100644
index 0000000..49f8ea5
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaMethod.java
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Stack;
+import java.util.Set;
+
+/**
+ * A method in SSA form
+ */
+public final class SsaMethod {
+    
+    /** basic blocks, indexed by block index */
+    private ArrayList<SsaBasicBlock> blocks;
+
+    /** Index of first executed block in method */
+    private int entryBlockIndex;
+
+    /**
+     * Index of exit block, which exists only in SSA form,
+     * or or -1 if there is none
+     */
+    private int exitBlockIndex;
+
+    private int registerCount;
+    private int spareRegisterBase;
+    private int borrowedSpareRegisters;
+
+    /** really one greater than the max label */
+    private int maxLabel;
+
+    /** the total width, in register-units, of the method's parameters */
+    private final int paramWidth;
+
+    /** true if this method has no 'this' pointer argument */
+    private final boolean isStatic;
+
+    /**
+     * indexed by register: the insn where said register is defined or null
+     * if undefined. null until (lazily) created.
+     */
+    private SsaInsn[] definitionList;
+
+    /** indexed by register: the list of all insns that use a register */
+    private ArrayList<SsaInsn>[] useList;
+    /** A version of useList with each List unmodifiable */
+    private List<SsaInsn>[] unmodifiableUseList;
+
+    /**
+     * "back-convert mode". Set during back-conversion when registers
+     * are about to be mapped into a non-SSA namespace. When true,
+     * use and def lists are unavailable.
+     *
+     * TODO remove this mode, plase the functionality elsewhere
+     */
+    private boolean backMode = false;
+
+    /**
+     * @param rmeth RopMethod to convert from
+     * @param paramWidth the total width, in register-units, of the
+     * method's parameters
+     * @param isStatic true if this method has no 'this' pointer argument
+     * @return SsaMethod representation
+     */
+    static SsaMethod newFromRopMethod(RopMethod rmeth, int paramWidth,
+            boolean isStatic) {
+        SsaMethod result;
+
+        result = new SsaMethod(paramWidth, isStatic);
+
+        result.maxLabel = rmeth.getBlocks().getMaxLabel();
+        result.registerCount = rmeth.getBlocks().getRegCount();
+        result.spareRegisterBase = result.registerCount;
+
+        result.convertRopToSsaBlocks(rmeth);
+
+        return result;
+    }
+
+    /**
+     * Builds a BitSet of block indices from a basic block list and a list
+     * of labels taken from Rop form
+     * @param blocks Rop blocks
+     * @param labelList list of rop block labels
+     * @return BitSet of block indices
+     */
+    static BitSet bitSetFromLabelList(BasicBlockList blocks,
+            IntList labelList) {
+
+        BitSet result;
+
+        result = new BitSet(blocks.size());
+
+        for (int i = 0, sz = labelList.size() ; i < sz ; i++) {
+            result.set(blocks.indexOfLabel(labelList.get(i)));
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds an IntList of block indices from a basic block list and a list
+     * of labels taken from Rop form
+     * @param ropBlocks Rop blocks
+     * @param labelList list of rop block labels
+     * @return IntList of block indices
+     */
+    public static IntList indexListFromLabelList(BasicBlockList ropBlocks,
+            IntList labelList) {
+
+        IntList result;
+
+        result = new IntList(labelList.size());
+
+        for (int i = 0, sz = labelList.size() ; i < sz ; i++) {
+            result.add(ropBlocks.indexOfLabel(labelList.get(i)));
+        }
+
+        return result;
+    }
+
+    private void convertRopToSsaBlocks(
+            RopMethod rmeth) {
+
+        BasicBlockList ropBlocks;
+
+        ropBlocks = rmeth.getBlocks();
+
+        blocks = new ArrayList<SsaBasicBlock>(ropBlocks.size() + 2);
+
+        for (int i = 0, sz = ropBlocks.size() ; i < sz ; i++) {
+            SsaBasicBlock sbb;
+
+            sbb = SsaBasicBlock.newFromRop(rmeth, i, this);
+
+            blocks.add(sbb);
+        }
+
+        // Add an no-op entry block
+        int origEntryBlockIndex = rmeth.getBlocks()
+                .indexOfLabel(rmeth.getFirstLabel());
+
+        SsaBasicBlock entryBlock
+                = blocks.get(origEntryBlockIndex).insertNewPredecessor();
+
+        entryBlockIndex = entryBlock.getIndex();
+        exitBlockIndex = -1; // this gets made later
+
+    }
+
+
+    /**
+     * Creates an exit block and attaches it to the CFG if this method
+     * exits. Methods that never exit will not have an exit block. This
+     * is called after edge-splitting and phi insertion, since the edges
+     * going into the exit block should not be considered in those steps.
+     */
+    void makeExitBlock() {
+        if (exitBlockIndex >= 0) {
+            throw new RuntimeException("must be called at most once");
+        }
+
+        exitBlockIndex = blocks.size();
+        SsaBasicBlock exitBlock
+                = new SsaBasicBlock(exitBlockIndex, maxLabel++, this);
+
+        blocks.add(exitBlock);
+
+        for (SsaBasicBlock block: blocks) {
+            block.exitBlockFixup(exitBlock);
+        }
+
+        if (exitBlock.getPredecessors().cardinality() == 0) {
+            // In cases where there is no exit...
+            blocks.remove(exitBlockIndex);
+            exitBlockIndex = -1;
+            maxLabel--;
+        }
+    }
+
+    /**
+     * Constructor
+     *
+     * @param paramWidth the total width, in register-units, of the
+     * method's parameters
+     * @param isStatic true if this method has no 'this' pointer argument
+     */
+    private SsaMethod(int paramWidth, boolean isStatic) {
+        this.paramWidth = paramWidth;
+        this.isStatic = isStatic;
+    }
+
+    /**
+     * Gets a new GOTO insn.
+     *
+     * @param block block to which this GOTO will be added
+     * (not it's destination!) 
+     * @return an appropriately-constructed instance.
+     */
+    private static SsaInsn getGoto(SsaBasicBlock block) {
+        return new NormalSsaInsn (
+                new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO,
+                    null, RegisterSpecList.EMPTY), block);
+    }
+    
+    /**
+     * Makes a new basic block for this method,
+     * which is empty besides a single <code>GOTO</code>. Successors and
+     * predecessors are not yet set.
+     *
+     * @return new block
+     */
+    public SsaBasicBlock makeNewGotoBlock() {
+        int newIndex = blocks.size();
+        SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this);
+
+        newBlock.getInsns().add(getGoto(newBlock));
+        blocks.add(newBlock);
+
+        return newBlock;
+    }
+
+    /**
+     * @return block index of first execution block
+     */
+    public int getEntryBlockIndex() {
+        return entryBlockIndex;
+    }
+
+    /**
+     * @return first execution block
+     */
+    public SsaBasicBlock getEntryBlock() {
+        return blocks.get(entryBlockIndex);
+    }
+
+    /**
+     * @return block index of exit block or -1 if there is none
+     */
+    public int getExitBlockIndex() {
+        return exitBlockIndex;
+    }
+
+    /**
+     * @return null-ok; block of exit block or null if there is none
+     */
+    public SsaBasicBlock getExitBlock() {
+        return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex);
+    }
+
+    /**
+     * @param bi block index or -1 for none
+     * @return rop label or -1 if bi was -1
+     */
+    public int blockIndexToRopLabel(int bi) {
+        if (bi < 0) {
+            return -1;
+        }
+        return blocks.get(bi).getRopLabel();
+    }
+
+    /**
+     * @return count of registers used in this method
+     */
+    public int getRegCount() {
+        return registerCount;
+    }
+
+    /**
+     * @return the total width, in register units, of the method's 
+     * parameters
+     */
+    public int getParamWidth() {
+        return paramWidth;
+    }
+
+    /**
+     * Returns true if this is a static method.
+     *
+     * @return true if this is a static method
+     */
+    public boolean isStatic() {
+        return isStatic;
+    }
+
+    /**
+     * Borrow a register to use as a temp. Used in the phi removal process.
+     * Call returnSpareRegisters() when done.
+     * @param category width (1 or 2) of the register
+     * @return register number to use
+     */
+    public int borrowSpareRegister(int category) {
+        int result;
+
+        result = spareRegisterBase + borrowedSpareRegisters;
+
+        borrowedSpareRegisters += category;
+
+        registerCount = Math.max(registerCount, result + category);
+
+        return result;
+    }
+
+    /**
+     * Returns all borrowed registers.
+     */
+    public void returnSpareRegisters() {
+        borrowedSpareRegisters = 0;
+    }
+
+    /**
+     * @return non-null; basic block list, do not modify.
+     */
+    public ArrayList<SsaBasicBlock> getBlocks() {
+        return blocks;
+    }
+
+    /**
+     * Returns the count of reachable blocks in this method: blocks that have
+     * predecessors (or are the start block)
+     *
+     * @return &gt;= 0; number of reachable basic blocks
+     */
+    public int getCountReachableBlocks() {
+        int ret = 0;
+
+        for (SsaBasicBlock b: blocks) {
+            // Blocks that have been disconnected don't count.
+            if (b.isReachable()) {
+                ret++;
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Remaps unversioned registers.
+     * @param mapper maps old registers to new.
+     */
+    public void mapRegisters(RegisterMapper mapper) {
+
+        for (SsaBasicBlock block: getBlocks()) {
+            for (SsaInsn insn: block.getInsns()) {
+                insn.mapRegisters(mapper);
+            }
+        }        
+        
+        registerCount = mapper.getNewRegisterCount();
+        spareRegisterBase = registerCount;
+    }
+
+    /**
+     * Returns the insn that defines the given register
+     * @param reg register in question
+     * @return insn (actual instance from code) that defined this reg or null
+     * if reg is not defined.
+     */
+    public SsaInsn getDefinitionForRegister(int reg) {
+        if (backMode) {
+            throw new RuntimeException("No def list in back mode");
+        }
+
+        if (definitionList != null) {
+            return definitionList[reg];
+        }
+
+        definitionList = new SsaInsn[getRegCount()];
+
+        forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn (NormalSsaInsn insn) {
+                definitionList[insn.getResult().getReg()] = insn;
+            }
+            public void visitPhiInsn (PhiInsn phi) {
+                definitionList[phi.getResult().getReg()] = phi;
+            }
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                RegisterSpec result = insn.getResult();
+                if (result != null) {
+                    definitionList[insn.getResult().getReg()] = insn;
+                }
+            }
+        });
+
+        return definitionList[reg];
+    }
+
+    /**
+     * Builds useList and unmodifiableUseList.
+     */
+    private void buildUseList() {
+        if (backMode) {
+            throw new RuntimeException("No use list in back mode");
+        }
+
+        useList = new ArrayList[registerCount];
+
+        for (int i = 0 ; i < registerCount; i++) {
+            useList[i] = new ArrayList();
+        }
+
+        forEachInsn(new SsaInsn.Visitor() {
+            /** {@inheritDoc} */
+            public void visitMoveInsn (NormalSsaInsn insn) {
+                addToUses(insn);
+            }
+            /** {@inheritDoc} */
+            public void visitPhiInsn (PhiInsn phi) {
+                addToUses(phi);
+            }
+            /** {@inheritDoc} */
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                addToUses(insn);
+            }
+            /**
+             * Adds specified insn to the uses list for all of its sources.
+             * @param insn non-null; insn to process
+             */
+            private void addToUses(SsaInsn insn) {
+                RegisterSpecList rl = insn.getSources();
+                int sz = rl.size();
+
+                for (int i = 0; i < sz; i++) {
+                    useList[rl.get(i).getReg()].add(insn);
+                }
+            }
+        });
+
+        unmodifiableUseList = new List[registerCount];
+
+        for (int i = 0 ; i < registerCount; i++) {
+            unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]);
+        }
+    }
+
+    /**
+     * Updates the use list for a single change in source register.
+     *
+     * @param insn non-null; insn being changed
+     * @param oldSource null-ok; The source that was used, if applicable
+     * @param newSource non-null; the new source being used
+     */
+    void onSourceChanged(SsaInsn insn,
+            RegisterSpec oldSource, RegisterSpec newSource) {
+
+        if (useList == null) return;
+        
+        if (oldSource != null) {
+            int reg = oldSource.getReg();
+            useList[reg].remove(insn);
+        }
+
+        int reg = newSource.getReg();
+        if (useList.length <= reg) {
+            useList = null;
+            return;
+        }
+        useList[reg].add(insn);
+    }
+
+    /**
+     * Updates the use list for a source list change.
+     *
+     * @param insn insn non-null; insn being changed. insn.getSources()
+     * must return the new source list.
+     * @param oldSources null-ok; list of sources that were previously used.
+     */
+    void onSourcesChanged(SsaInsn insn, RegisterSpecList oldSources) {
+        if (useList == null) return;
+
+        if (oldSources != null) {
+            removeFromUseList(insn, oldSources);
+        }
+
+        RegisterSpecList sources = insn.getSources();
+        int szNew = sources.size();
+
+        for(int i = 0; i < szNew; i++) {
+            int reg = sources.get(i).getReg();
+            useList[reg].add(insn);
+        }        
+    }
+
+    /**
+     * Removes a given <code>insn</code> from the use lists for the given
+     * <code>oldSources</code> (rather than the sources currently
+     * returned by insn.getSources()).
+     *
+     * @param insn non-null; insn in question
+     * @param oldSources null-ok; registers whose use lists <code>insn</code>
+     * should be removed form.
+     */
+    private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) {
+        if (oldSources == null) {
+            return;
+        }
+        int szNew = oldSources.size();
+        for(int i = 0; i < szNew; i++) {
+            if (!useList[oldSources.get(i).getReg()].remove(insn)) {
+                throw new RuntimeException("use not found");
+            }
+        }
+    }
+
+    /**
+     * Adds an insn to both the use and def lists. For use when adding
+     * a new insn to the method.
+     *
+     * @param insn non-null; insn to add
+     */
+    void onInsnAdded(SsaInsn insn) {
+        onSourcesChanged(insn, null);
+        updateOneDefinition(insn, null);
+    }
+
+    /**
+      * Removes an instruction from use and def lists. For use during
+      * instruction removal.
+      *
+      * @param insn non-null; insn to remove.
+      */
+     void onInsnRemoved(SsaInsn insn) {
+         if (useList != null) {
+             removeFromUseList(insn, insn.getSources());
+         }
+
+         RegisterSpec resultReg = insn.getResult();
+         if (definitionList != null && resultReg != null) {
+             definitionList[resultReg.getReg()] = null;
+         }
+     }
+
+    /**
+     * Indicates that the instruction list has changed or the SSA register
+     * count has increased, so that internal datastructures that rely on
+     * it should be rebuild. In general, the various other on* methods
+     * should be called in preference when changes occur if they are 
+     * applicable.
+     */
+    public void onInsnsChanged() {
+        // Definition list will need to be recomputed
+        definitionList = null;
+
+        // Use list will need to be recomputed
+        useList = null;
+        unmodifiableUseList = null;
+    }
+
+    /**
+     * Updates a single definition.
+     *
+     * @param insn non-null; insn who's result should be recorded as
+     * a definition
+     * @param oldResult null-ok; a previous result that should be no longer
+     * considered a definition by this insn
+     */
+    void updateOneDefinition(SsaInsn insn, RegisterSpec oldResult) {
+        if (definitionList == null) return;
+        if (oldResult != null) {
+            int reg = oldResult.getReg();
+            definitionList[reg] = null;
+        }
+
+        RegisterSpec resultReg = insn.getResult();
+        if (resultReg != null) {
+            int reg = resultReg.getReg();
+
+            if (definitionList[reg] != null) {
+                throw new RuntimeException("Duplicate add of insn");
+            } else {
+                definitionList[resultReg.getReg()] = insn;
+            }
+        }
+    }
+
+    /**
+     * Returns the list of all source uses (not results) for a register
+     * @param reg register in question
+     * @return unmodifiable instruction list
+     */
+    public List<SsaInsn> getUseListForRegister(int reg) {
+
+        if (unmodifiableUseList == null) {
+            buildUseList();
+        }
+
+        return unmodifiableUseList[reg];
+    }
+
+    /**
+     * Returns a modifiable copy of the register use list.
+     * @return modifiable copy of the use-list, indexed by register
+     */
+    public ArrayList<SsaInsn>[] getUseListCopy() {
+        if (useList == null) {
+            buildUseList();
+        }
+
+        ArrayList<SsaInsn>[] useListCopy
+                = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]);
+
+        for (int i = 0; i < registerCount; i++) {
+            useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i]));
+        }
+
+        return useListCopy;
+    }
+
+    /**
+     * Checks to see if the given SSA reg is ever associated with a local
+     * local variable. Each SSA reg may be associated with at most one
+     * local var.
+     *
+     * @param spec non-null; ssa reg
+     * @return true if reg is ever associated with a local
+     */
+    public boolean isRegALocal(RegisterSpec spec) {
+        SsaInsn defn = getDefinitionForRegister(spec.getReg());
+
+        if (defn == null) {
+            // version 0 registers are never used as locals
+            return false;
+        }
+
+        // Does the definition have a local associated with it?
+        if (defn.getLocalAssignment() != null) return true;
+
+        // If not, is there a mark-local insn?
+        for (SsaInsn use: getUseListForRegister(spec.getReg())) {
+            Insn insn = use.getOriginalRopInsn();
+
+            if (insn != null
+                    && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+
+    /**
+     * Sets the new register count after renaming.
+     * @param newRegCount new register count
+     */
+    /*package*/ void setNewRegCount(int newRegCount) {
+        registerCount = newRegCount;
+        spareRegisterBase = registerCount;
+        onInsnsChanged();
+    }
+
+    /**
+     * Makes a new SSA register. For use after renaming has completed.
+     *
+     * @return &gt;=0 new SSA register.
+     */
+    public int makeNewSsaReg() {
+        int reg = registerCount++;
+        spareRegisterBase = registerCount;
+        onInsnsChanged();
+        return reg;        
+    }
+
+    /**
+     * Visit all insns in this method
+     * @param visitor non-null; callback interface
+     */
+    public void forEachInsn(SsaInsn.Visitor visitor) {
+        for (SsaBasicBlock block: blocks) {
+            block.forEachInsn(visitor);
+        }
+    }
+
+    /**
+     * Visits each phi insn in this method
+     * @param v non-null; callback
+     */
+    public void forEachPhiInsn(PhiInsn.Visitor v) {
+        for (SsaBasicBlock block: blocks) {
+            block.forEachPhiInsn(v);
+        }
+    }
+
+
+    /**
+     * Walk the basic block tree in depth-first order, calling the visitor
+     * method once for every block. This depth-first walk may be run forward
+     * from the method entry point or backwards from the method exit points.
+     * @param reverse true if this should walk backwards from the exit points
+     * @param v non-null; callback interface. <code>parent</code>is set
+     * unless this is the root node
+     */
+    public void forEachBlockDepthFirst(boolean reverse,
+            SsaBasicBlock.Visitor v) {
+        BitSet visited = new BitSet(blocks.size());
+        // We push the parent first, then the child on the stack
+        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+        SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock();
+
+        if (rootBlock == null) {
+            // in the case there's no exit block
+            return;
+        }
+
+        stack.add(null);    // start with null parent
+        stack.add(rootBlock);
+
+        while (stack.size() > 0) {
+            SsaBasicBlock cur = stack.pop();
+            SsaBasicBlock parent = stack.pop();
+
+            if (!visited.get(cur.getIndex())) {
+                BitSet children
+                        = reverse ? cur.getPredecessors() : cur.getSuccessors();
+                for (int i = children.nextSetBit(0); i >= 0
+                        ; i = children.nextSetBit(i + 1)) {
+                    stack.add(cur);
+                    stack.add(blocks.get(i));
+                }
+                visited.set(cur.getIndex());
+                v.visitBlock(cur, parent);
+            }
+        }
+    }
+
+    /**
+     * Visits blocks in dom-tree order, starting at the current node.
+     * The <code>parent</code> parameter of the Visitor.visitBlock callback
+     * is currently always set to null.
+     *
+     * @param v non-null; callback interface
+     */
+    public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) {
+        BitSet visited = new BitSet(getBlocks().size());
+        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+        stack.add(getEntryBlock());
+
+        while (stack.size() > 0) {
+            SsaBasicBlock cur = stack.pop();
+            ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren();
+
+            if (!visited.get(cur.getIndex())) {
+                // We walk the tree this way for historical reasons...
+                for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+                    SsaBasicBlock child = curDomChildren.get(i);
+                    stack.add(child);
+                }
+                visited.set(cur.getIndex());
+                v.visitBlock(cur, null);
+            }
+        }
+    }
+
+    /**
+     * Deletes all insns in the set from this method
+     *
+     * @param deletedInsns non-null; insns to delete
+     */
+    public void deleteInsns(Set<SsaInsn> deletedInsns) {
+        for (SsaBasicBlock block: getBlocks()) {
+            ArrayList<SsaInsn> insns = block.getInsns();
+
+            for (int i = insns.size() - 1; i >= 0; i--) {
+                SsaInsn insn = insns.get(i);
+
+                if (deletedInsns.contains(insn)) {
+                    onInsnRemoved(insn);
+                    insns.remove(i);
+                }
+            }
+
+            // Check to see if we need to add a GOTO
+
+            int insnsSz = insns.size();
+            SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1);
+
+            if (block != getExitBlock() && (insnsSz == 0
+                    || lastInsn.getOriginalRopInsn() == null
+                    || lastInsn.getOriginalRopInsn().getOpcode()
+                        .getBranchingness() == Rop.BRANCH_NONE)) {
+                // We managed to eat a throwable insn
+
+                Insn gotoInsn = new PlainInsn(Rops.GOTO,
+                        SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY);
+                insns.add(SsaInsn.makeFromRop(gotoInsn, block));
+            }
+        }
+    }
+
+    /**
+     * Set "back-convert mode". Set during back-conversion when registers
+     * are about to be mapped into a non-SSA namespace. When true,
+     * use and def lists are unavailable.
+     */
+    public void setBackMode() {
+        backMode = true;
+        useList = null;
+        definitionList = null;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaRenamer.java b/dx/src/com/android/dx/ssa/SsaRenamer.java
new file mode 100644
index 0000000..64bad2c
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaRenamer.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.HashMap;
+
+/**
+ * Complete transformation to SSA form by renaming all registers accessed.<p>
+ *
+ * See Appel algorithm 19.7<p>
+ *
+ * Unlike the original algorithm presented in Appel, this renamer converts
+ * to a new flat (versionless) register space. The "version 0" registers,
+ * which represent the initial state of the Rop registers and should never
+ * actually be meaningfully accessed in a legal program, are represented
+ * as the first N registers in the SSA namespace. Subsequent assignments
+ * are assigned new unique names. Note that the incoming Rop representation
+ * has a concept of register widths, where 64-bit values are stored into
+ * two adjoining Rop registers. This adjoining register representation is
+ * ignored in SSA form conversion and while in SSA form, each register can be e
+ * either 32 or 64 bits wide depending on use. The adjoining-register
+ * represention is re-created later when converting back to Rop form. <p>
+ *
+ * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP
+ * representation means that unaligned accesses to 64-bit registers are not
+ * supported. For example, you cannot do a 32-bit operation on a portion of
+ * a 64-bit register. This will never be observed to happen when coming
+ * from Java code, of course.<p>
+ *
+ * The implementation here, rather than keeping a single register version
+ * stack for the entire method as the dom tree is walked, instead keeps
+ * a mapping table for the current block being processed. Once the
+ * current block has been processed, this mapping table is then copied
+ * and used as the initial state for child blocks.<p>
+ */
+class SsaRenamer implements Runnable {
+
+    private static final boolean DEBUG = false;
+
+    /** Method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /** next available SSA register */
+    private int nextSsaReg;
+
+    /** the number of original rop registers */
+    private final int ropRegCount;
+
+    /**
+     * Indexed by block index; register version state for each block start.
+     * This list is updated by each dom parent for its children. The only
+     * sub-arrays that exist at any one time are the start states for blocks
+     * yet to be processed by a <code>BlockRenamer</code> instance.
+     */
+    private final RegisterSpec[][] startsForBlocks;
+
+    /** map of SSA register number to debug (local var names) or null of n/a */
+    private final ArrayList<LocalItem> ssaRegToLocalItems;
+
+    /**
+     * Maps SSA registers back to the original rop number.
+     * Used for debug only.
+     */
+    private IntList ssaRegToRopReg;
+
+    /**
+     * Constructs an instance of the renamer
+     *
+     * @param ssaMeth non-null; un-renamed SSA method that will
+     * be renamed.
+     */
+    SsaRenamer (final SsaMethod ssaMeth) {
+        ropRegCount = ssaMeth.getRegCount();
+
+        this.ssaMeth = ssaMeth;
+        /*
+         * Reserve the first N registers in the SSA register space for
+         * "version 0" registers
+         */
+        nextSsaReg = ropRegCount;
+        startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][];
+
+        ssaRegToLocalItems = new ArrayList<LocalItem>();
+
+        if (DEBUG) {
+            ssaRegToRopReg = new IntList(ropRegCount);
+        }
+
+        /*
+         * Appel 19.7
+         *
+         * Initialization:
+         *   for each variable a        // register i
+         *      Count[a] <- 0           // nextSsaReg, flattened
+         *      Stack[a] <- 0           // versionStack
+         *      push 0 onto Stack[a]
+         *
+         */
+
+        // top entry for the version stack is version 0
+        RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount];
+        for (int i = 0; i < ropRegCount; i++) {
+            // everyone starts with a version 0 register
+            initialRegMapping[i] = RegisterSpec.make(i, Type.VOID);
+
+            if (DEBUG) {
+                ssaRegToRopReg.add(i);
+            }
+        }
+
+        // Initial state for entry block
+        startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping;
+    }
+
+    /**
+     * Performs renaming transformation, modifying the method's instructions
+     * in-place.
+     */
+    public void run() {
+
+        // Rename each block in dom-tree DFS order.
+        ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+            public void visitBlock (SsaBasicBlock block, SsaBasicBlock unused) {
+                new BlockRenamer(block).process();
+            }
+        });
+
+        ssaMeth.setNewRegCount(nextSsaReg);
+        ssaMeth.onInsnsChanged();
+
+        if (DEBUG) {
+            System.out.println("SSA\tRop");
+            // We're going to compute the version of the rop register
+            // by keeping a running total of how many times the rop register
+            // has been mapped.
+            int[] versions = new int[ropRegCount];
+
+            int sz = ssaRegToRopReg.size();
+            for(int i = 0; i < sz; i++) {
+                int ropReg =  ssaRegToRopReg.get(i);
+                System.out.println(i +"\t" + ropReg + "["
+                        + versions[ropReg] + "]");
+                versions[ropReg]++;
+            }
+        }
+    }
+
+    /**
+     * Duplicates a RegisterSpec array
+     * @param orig non-null; array to duplicate
+     * @return non-null; new instance
+     */
+    private static  RegisterSpec[] dupArray(RegisterSpec[] orig) {
+        RegisterSpec[] copy = new RegisterSpec[orig.length];
+
+        System.arraycopy(orig, 0, copy, 0, orig.length);
+
+        return copy;
+    }
+
+    /**
+     * Gets a local variable item for a specified register.
+     *
+     * @param ssaReg register in SSA name space
+     * @return null-ok; Local variable name or null if none
+     */
+    private LocalItem getLocalForNewReg(int ssaReg) {
+        if (ssaReg < ssaRegToLocalItems.size()) {
+            return ssaRegToLocalItems.get(ssaReg);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Records a debug (local variable) name for a specified register.
+     *
+     * @param ssaReg non-null named register spec in SSA name space
+     */
+    private void setNameForSsaReg(RegisterSpec ssaReg) {
+        int reg = ssaReg.getReg();
+        LocalItem local = ssaReg.getLocalItem();
+
+        ssaRegToLocalItems.ensureCapacity(reg + 1);
+        while (ssaRegToLocalItems.size() <= reg) {
+            ssaRegToLocalItems.add(null);
+        }
+
+        ssaRegToLocalItems.set(reg, local);
+    }
+
+    /**
+     * Returns true if this SSA register is a "version 0"
+     * register. All version 0 registers are assigned the first N register
+     * numbers, where N is the count of original rop registers.
+     *
+     * @param ssaReg the SSA register in question
+     * @return true if it is a version 0 register.
+     */
+    private boolean isVersionZeroRegister(int ssaReg) {
+        return ssaReg < ropRegCount;
+    }
+
+    /**
+     * Returns true if a and b are equal or are both null
+
+     * @param a null-ok
+     * @param b null-ok
+     * @return Returns true if a and b are equal or are both null
+     */
+    private static boolean equalsHandlesNulls(Object a, Object b) {
+        return a == b ||  (a != null && a.equals(b));
+    }
+
+    /**
+     * Processes all insns in a block and renames their registers
+     * as appropriate.
+     */
+    private class BlockRenamer implements SsaInsn.Visitor{
+        /** non-null; block we're processing. */
+        private final SsaBasicBlock block;
+
+        /**
+         * non-null; indexed by old register name. The current top of the
+         * version stack as seen by this block. It's initialized from
+         * the ending state of its dom parent, updated as the block's
+         * instructions are processed, and then copied to each one of its
+         * dom children.
+         */
+        private final RegisterSpec[] currentMapping;
+
+        /**
+         * Contains the set of moves we need to keep
+         * to preserve local var info. All other moves will be deleted.
+         */
+        private final HashSet<SsaInsn> movesToKeep;
+
+        /**
+         * Maps the set of insns to replace after renaming is finished
+         * on the block.
+         */
+        private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
+
+        private final RenamingMapper mapper;
+
+        /**
+         * Constructs a block renamer instance. Call <code>process</code>
+         * to process.
+         *
+         * @param block non-null; block to process
+         */
+        BlockRenamer(final SsaBasicBlock block) {
+            this.block = block;
+            currentMapping = startsForBlocks[block.getIndex()];
+            movesToKeep = new HashSet<SsaInsn>();
+            insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
+            mapper =  new RenamingMapper();
+
+            // We don't need our own start state anymore
+            startsForBlocks[block.getIndex()] = null;
+        }
+
+        /**
+         * Provides a register mapping between the old register space
+         * and the current renaming mapping. The mapping is updated
+         * as the current block's instructions are processed.
+         */
+        private class RenamingMapper extends RegisterMapper {
+
+            RenamingMapper() {
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public int getNewRegisterCount() {
+                return nextSsaReg;
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public RegisterSpec map(RegisterSpec registerSpec) {
+                if (registerSpec == null) return null;
+
+                int reg = registerSpec.getReg();
+
+                // for debugging: assert that the mapped types are compatible
+                if(DEBUG) {
+                    RegisterSpec newVersion = currentMapping[reg];
+                    if (newVersion.getBasicType() != Type.BT_VOID
+                            && registerSpec.getBasicFrameType()
+                                != newVersion.getBasicFrameType()) {
+
+                        throw new RuntimeException(
+                                "mapping registers of incompatible types! "
+                                + registerSpec
+                                + " " + currentMapping[reg]);
+                    }
+                }
+
+                return registerSpec.withReg(currentMapping[reg].getReg());
+            }
+        }
+
+        /**
+         * Renames all the variables in this block and inserts appriopriate
+         * phis in successor blocks.
+         */
+        public void process() {
+            /*
+             * From Appel:
+             *
+             * Rename(n) =
+             *   for each statement S in block n   // 'statement' in 'block'
+             */
+
+            block.forEachInsn(this);
+
+            updateSuccessorPhis();
+
+            // Delete all move insns in this block
+            ArrayList<SsaInsn> insns = block.getInsns();
+            int szInsns = insns.size();
+
+            for (int i = szInsns - 1; i >= 0 ; i--) {
+                SsaInsn insn = insns.get(i);
+                SsaInsn replaceInsn;
+
+                replaceInsn = insnsToReplace.get(insn);
+
+                if (replaceInsn != null) {
+                    insns.set(i, replaceInsn);                    
+                } else if (insn.isNormalMoveInsn()
+                        && !movesToKeep.contains(insn)) {
+                    insns.remove(i);
+                }
+            }
+
+            // Store the start states for our dom children
+            boolean first = true;
+            for (SsaBasicBlock child: block.getDomChildren()) {
+                if (child != block) {
+                    RegisterSpec[] childStart;
+
+                    // don't bother duplicating the array for the first child
+                    childStart = first ? currentMapping
+                            : dupArray(currentMapping);
+
+                    startsForBlocks[child.getIndex()] = childStart;
+                    first = false;
+                }
+            }
+
+            // currentMapping is owned by a child now
+        }
+
+        /**
+         * Enforces a few contraints when a register mapping is added.
+         *
+         * <ol>
+         * <li> Ensures that all new SSA registers specs in the mapping
+         * table with the same register number are identical. In effect, once
+         * an SSA register spec has received or lost a local variable name,
+         * then every old-namespace register that maps to it should gain or
+         * lose its local variable name as well.
+         * <li> Records the local name associated with the
+         * register so that a register is never associated with more than one
+         * local.
+         * <li> ensures that only one SSA register
+         * at a time is considered to be associated with a local variable. When
+         * <code>currentMapping</code> is updated and the newly added element
+         * is named, strip that name from any other SSA registers.
+         * </ol>
+         *
+         * @param ropReg &gt;= 0 Rop register number
+         * @param ssaReg non-null; An SSA register that has just
+         * been added to <code>currentMapping</code>
+         */
+        private void addMapping(int ropReg, RegisterSpec ssaReg) {
+            int ssaRegNum = ssaReg.getReg();
+            LocalItem ssaRegLocal = ssaReg.getLocalItem();
+
+            currentMapping[ropReg] = ssaReg;
+
+            /*
+             * Ensure all SSA register specs with the same reg are identical.
+             */
+            for (int i = currentMapping.length - 1; i >= 0; i--) {
+                RegisterSpec cur = currentMapping[i];
+
+                if (ssaRegNum == cur.getReg()) {
+                    currentMapping[i] = ssaReg;
+                }
+            }
+
+            // All further steps are for registers with local information
+            if (ssaRegLocal == null) {
+                return;
+            }
+
+            // Record that this SSA reg has been associated with a local
+            setNameForSsaReg(ssaReg);
+
+            // Ensure that no other SSA regs are associated with this local
+            for (int i = currentMapping.length - 1; i >= 0; i--) {
+                RegisterSpec cur = currentMapping[i];
+
+                if (ssaRegNum != cur.getReg()
+                        && ssaRegLocal.equals(cur.getLocalItem())) {
+                    currentMapping[i] = cur.withLocalItem(null);
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * Phi insns have their result registers renamed.
+         * */
+        public void visitPhiInsn(PhiInsn phi) {
+            /* don't process sources for phi's */
+            processResultReg(phi);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * Move insns are treated as a simple mapping operation, and
+         * will later be removed unless they represent a local variable
+         * assignment. If they represent a local variable assignement, they
+         * are preserved.
+         */
+        public void visitMoveInsn(NormalSsaInsn insn) {
+            /*
+             * for moves: copy propogate the move if we can, but don't
+             * if we need to preserve local variable info and the
+             * result has a different name than the source.
+             */
+
+            RegisterSpec ropResult = insn.getResult();
+            int ropResultReg = ropResult.getReg();
+            int ropSourceReg = insn.getSources().get(0).getReg();
+
+            insn.mapSourceRegisters(mapper);
+            int ssaSourceReg = insn.getSources().get(0).getReg();
+
+            LocalItem sourceLocal = currentMapping[ropSourceReg].getLocalItem();
+            LocalItem resultLocal = ropResult.getLocalItem();
+
+            /*
+             * A move from a register that's currently associated with a local
+             * to one that will not be associated with a local does not need
+             * to be preserved, but the local association should remain.
+             * Hence, we inherit the sourceLocal where the resultLocal is null.
+             */
+
+            LocalItem newLocal
+                    = (resultLocal == null) ? sourceLocal : resultLocal;
+
+            LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
+
+            // If we take the new local, will only one local have ever
+            // been associated with this SSA reg?
+            boolean onlyOneAssociatedLocal
+                    = associatedLocal == null || newLocal == null
+                    || newLocal.equals(associatedLocal);
+                    
+            /*
+             * If we're going to copy-propogate, then the ssa register spec
+             * that's going to go into the mapping is made up of the
+             * source register number mapped from above, the type
+             * of the result, and the name either from the result (if
+             * specified) or inherited from the existing mapping.
+             *
+             * The move source has incomplete type information
+             * in null object cases, so the result type is used.
+             */
+            RegisterSpec ssaReg
+                    = RegisterSpec.makeLocalOptional(
+                        ssaSourceReg, ropResult.getType(), newLocal);
+
+            if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
+                    && equalsHandlesNulls(newLocal, sourceLocal))) {
+                /*
+                 * We don't have to keep this move to preserve local
+                 * information. Either the name is the same, or the result
+                 * register spec is unnamed.
+                 */
+
+                addMapping(ropResultReg, ssaReg);
+            } else if (onlyOneAssociatedLocal && sourceLocal == null) {
+
+                /*
+                 * The register was previously unnamed. This means that a
+                 * local starts after it's first assignment in SSA form
+                 */
+
+                RegisterSpecList ssaSources;
+
+                ssaSources = RegisterSpecList.make(
+                        RegisterSpec.make(ssaReg.getReg(),
+                                ssaReg.getType(), newLocal));
+
+                SsaInsn newInsn
+                        = SsaInsn.makeFromRop(
+                            new PlainInsn(Rops.opMarkLocal(ssaReg),
+                            SourcePosition.NO_INFO, null, ssaSources),block);
+
+                insnsToReplace.put(insn, newInsn);
+
+                // Just map as above
+                addMapping(ropResultReg, ssaReg);
+            } else {
+                /*
+                 * Do not copy-propogate, since the two registers
+                 * have two different local-variable names
+                 */
+                processResultReg(insn);
+
+                movesToKeep.add(insn);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * All insns that are not move or phi insns have their source registers
+         * mapped ot the current mapping. Their result registers are then
+         * renamed to a new SSA register which is then added to the current
+         * register mapping.
+         */
+        public void visitNonMoveInsn(NormalSsaInsn insn) {
+            /* for each use of some variable X in S */
+            insn.mapSourceRegisters(mapper);
+
+            processResultReg(insn);
+        }
+
+        /**
+         * Renames the result register of this insn and updates the
+         * current register mapping. Does nothing if this insn has no result.
+         * Applied to all non-move insns.
+         *
+         * @param insn insn to process.
+         */
+        void processResultReg(SsaInsn insn) {
+            RegisterSpec ropResult = insn.getResult();
+
+            if (ropResult == null) {
+                return;
+            }
+
+            int ropReg = ropResult.getReg();
+
+            insn.changeResultReg(nextSsaReg);
+            addMapping(ropReg, insn.getResult());
+
+            if (DEBUG) {
+                ssaRegToRopReg.add(ropReg);
+            }
+
+            nextSsaReg++;
+        }
+
+        /**
+         * Updates the phi insns in successor blocks with operands based
+         * on the current mapping of the rop register the phis represent.
+         */
+        private void updateSuccessorPhis() {
+            PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
+                public void visitPhiInsn (PhiInsn insn) {
+                    int ropReg;
+
+                    ropReg = insn.getRopResultReg();
+
+                    /*
+                     * Never add a version 0 register as a phi operand.
+                     * Version 0 registers represent the initial register state,
+                     * and thus are never significant. Furthermore,
+                     * the register liveness algorithm doesn't properly
+                     * count them as "live in" at the beginning of the method.
+                     */
+
+                    RegisterSpec stackTop = currentMapping[ropReg];
+                    if (!isVersionZeroRegister(stackTop.getReg())) {
+                        insn.addPhiOperand(stackTop, block);
+                    }
+                }
+            };
+
+            BitSet successors = block.getSuccessors();
+            for (int i = successors.nextSetBit(0); i >= 0;
+                    i = successors.nextSetBit(i + 1)) {
+
+                SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
+
+                successor.forEachPhiInsn(visitor);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/_tests/_DomFront.java b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
new file mode 100644
index 0000000..997da21
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa._tests;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class <code>com.android.dx.ssa.DomFront</code>.
+ */
+public class _DomFront
+        extends TestCase {
+
+    public void test_one() {
+
+    }
+
+
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
new file mode 100644
index 0000000..d3ff7c7
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Allocates registers via a naive n^2 register allocator.
+ * This allocator does not try to co-locate local variables or deal
+ * intelligently with different size register uses.
+ */
+public class FirstFitAllocator extends RegisterAllocator {
+    /**
+     * If true, allocator places parameters at the top of the frame
+     * in calling-convention order.
+     */
+    private static final boolean PRESLOT_PARAMS = true;
+
+    /** indexed by old reg; the set of old regs we've mapped */
+    private final BitSet mapped;
+
+    /** {@inheritDoc} */
+    public FirstFitAllocator(
+            final SsaMethod ssaMeth, final InterferenceGraph interference) {
+        super(ssaMeth, interference);
+
+        mapped = new BitSet(ssaMeth.getRegCount());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        return PRESLOT_PARAMS;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+        int oldRegCount = ssaMeth.getRegCount();
+
+        BasicRegisterMapper mapper
+                = new BasicRegisterMapper(oldRegCount);
+
+        int nextNewRegister = 0;
+
+        if (PRESLOT_PARAMS) {
+            /*
+             * Reserve space for the params at the bottom of the register
+             * space. Later, we'll flip the params to the end of the register
+             * space.
+             */
+
+            nextNewRegister = ssaMeth.getParamWidth();
+        }
+
+        for (int i = 0; i < oldRegCount; i++) {
+            if (mapped.get(i)) {
+                // we already got this one
+                continue;
+            }
+
+            int maxCategory = getCategoryForSsaReg(i);
+            IntSet current = new BitIntSet(oldRegCount);
+
+            interference.mergeInterferenceSet(i, current);
+
+            boolean isPreslotted = false;
+            int newReg = 0;
+
+            if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) {
+                // Any move-param definition must be a NormalSsaInsn
+                NormalSsaInsn defInsn = (NormalSsaInsn)
+                       ssaMeth.getDefinitionForRegister(i);
+
+                newReg = paramNumberFromMoveParam(defInsn);
+
+                mapper.addMapping(i, newReg, maxCategory);
+                isPreslotted = true;
+            } else {
+                mapper.addMapping(i, nextNewRegister, maxCategory);
+                newReg = nextNewRegister;
+            }
+
+            for (int j = i + 1; j < oldRegCount; j++) {
+
+                if (mapped.get(j) || isDefinitionMoveParam(j)) {
+                    continue;
+                }
+
+                /*
+                 * If reg j doesn't interfere with the current mapping.
+                 * Also, if this is a pre-slotted method parameter, we
+                 * can't use more than the original param width.
+                 */
+                if (!current.has(j)
+                        && !(isPreslotted
+                            && (maxCategory < getCategoryForSsaReg(j)))) {
+
+                    interference.mergeInterferenceSet(j, current);
+
+                    maxCategory = Math.max(maxCategory,
+                            getCategoryForSsaReg(j));
+
+                    mapper.addMapping(j, newReg, maxCategory);
+                    mapped.set(j);
+                }
+            }
+
+            mapped.set(i);
+            if (!isPreslotted) {
+                nextNewRegister += maxCategory;
+            }
+        }
+
+        return mapper;
+    }
+
+    /**
+     * Returns the parameter number that this move-param insn refers to
+     * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown)
+     * @return parameter number (offset in the total parameter width)
+     */
+    private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) {
+        CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn();
+
+        return ((CstInteger) origInsn.getConstant()).getValue();
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
new file mode 100644
index 0000000..14eac90
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.InterferenceRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Allocates registers in a first-fit fashion, with the bottom reserved for
+ * method parameters and all SSAregisters representing the same local variable
+ * kept together if possible.
+ */
+public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
+    private static final boolean DEBUG = false;
+
+    /** maps local variable to a list of associated SSA registers*/
+    private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
+
+    /** list of move-result-pesudo instructions seen in this method */
+    private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
+
+    /** list of invoke-range instructions seen in this method */
+    private final ArrayList<NormalSsaInsn> invokeRangeInsns;
+
+    /** indexed by SSA reg; the set of SSA regs we've mapped */
+    private final BitSet ssaRegsMapped;
+
+    /** Register mapper which will be our result */
+    private final InterferenceRegisterMapper mapper;
+
+    /** end of rop registers range (starting at 0) reserved for parameters. */
+    private final int paramRangeEnd;
+
+    /** set of Rop registers reserved for parameters or local variables. */
+    private final BitSet reservedRopRegs;
+
+    /** set of Rop registers that have been used by anything.*/
+    private final BitSet usedRopRegs;
+
+    /** true if converter should take steps to minimize rop-form registers*/
+    private final boolean minimizeRegisters;
+
+
+    /**
+     * Constructs instance.
+     *
+     * @param ssaMeth non-null; method to process
+     * @param interference non-null interference graph for SSA registers
+     * @param minimizeRegisters true if converter should take steps to
+     * minimize rop-form registers
+     */
+    public FirstFitLocalCombiningAllocator(
+            final SsaMethod ssaMeth, InterferenceGraph interference,
+            boolean minimizeRegisters) {
+        super(ssaMeth, interference);
+
+        ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
+
+        mapper = new InterferenceRegisterMapper(
+                interference, ssaMeth.getRegCount());
+
+        this.minimizeRegisters = minimizeRegisters;
+
+        /*
+         * Reserve space for the params at the bottom of the register
+         * space. Later, we'll flip the params to the end of the register
+         * space.
+         */
+
+        paramRangeEnd = ssaMeth.getParamWidth();
+
+        reservedRopRegs = new BitSet(paramRangeEnd * 2);
+        reservedRopRegs.set(0, paramRangeEnd);
+        usedRopRegs = new BitSet(paramRangeEnd * 2);
+        localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
+        moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
+        invokeRangeInsns = new ArrayList<NormalSsaInsn>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+
+        analyzeInstructions();
+
+        if (DEBUG) {
+            printLocalVars();
+        }
+
+        if(DEBUG) System.out.println("--->Mapping local-associated params");
+        handleLocalAssociatedParams();
+
+        if(DEBUG) System.out.println("--->Mapping other params");
+        handleUnassociatedParameters();
+
+        if(DEBUG) System.out.println("--->Mapping invoke-range");
+        handleInvokeRangeInsns();
+        
+        if(DEBUG) System.out.println("--->Mapping local-associated non-params");
+        handleLocalAssociatedOther();
+
+        if(DEBUG) System.out.println("--->Mapping check-cast results");
+        handleCheckCastResults();
+
+        if(DEBUG) System.out.println("--->Mapping others");
+        handleNormalUnassociated();
+
+        return mapper;
+    }
+
+    /**
+     * Dumps local variable table to stdout for debugging.
+     */
+    private void printLocalVars() {
+        System.out.println("Printing local vars");
+        for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e:
+                localVariables.entrySet()) {
+            StringBuilder regs = new StringBuilder();
+
+            regs.append('{');
+            regs.append(' ');
+            for(RegisterSpec reg: e.getValue()) {
+                regs.append('v');
+                regs.append(reg.getReg());
+                regs.append(' ');
+            }
+            regs.append('}');
+            System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
+        }
+    }
+
+    /**
+     * Maps all local-associated parameters to Rop registers.
+     */
+    private void handleLocalAssociatedParams() {
+        for (ArrayList<RegisterSpec> ssaRegs: localVariables.values()) {
+            int sz = ssaRegs.size();
+            int paramIndex = -1;
+            int paramCategory = 0;
+
+            // First, find out if this local variable is a parameter
+            for (int i = 0 ; i < sz ; i++) {
+                RegisterSpec ssaSpec = ssaRegs.get(i);
+                int ssaReg = ssaSpec.getReg();
+
+                paramIndex = getParameterIndexForReg(ssaReg);
+
+                if (paramIndex >= 0) {
+                    paramCategory = ssaSpec.getCategory();
+                    addMapping(ssaSpec, paramIndex);
+                    break;
+                }
+            }
+
+            if (paramIndex < 0) {
+                // this local wasn't a parameter
+                continue;
+            }
+
+            // Any remaining local-associated registers will be mapped later
+            tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
+        }
+    }
+
+    /**
+     * Gets the parameter index for SSA registers that are method parameters.
+     * -1 is returned for non-parameter registers.
+     *
+     * @param ssaReg &gt;=0 SSA register to look up
+     * @return parameter index or -1 if not a parameter
+     */
+    private int getParameterIndexForReg(int ssaReg) {
+        SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
+        if (defInsn == null) {
+            return -1;
+        }
+        
+        Rop opcode = defInsn.getOpcode();
+
+        // opcode == null for phi insns
+        if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
+            CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
+            return  ((CstInteger) origInsn.getConstant()).getValue();
+        }
+
+        return -1;
+    }
+
+    /**
+     * Maps all local-associated registers that are not parameters. Tries to
+     * find an unreserved range that's wide enough for all of the SSA registers,
+     * and then tries to map them all to that range. If not all fit,
+     * a new range is tried until all registers have been fit.
+     */
+    private void handleLocalAssociatedOther() {
+        for (ArrayList<RegisterSpec> specs: localVariables.values()) {
+            int ropReg = 0;
+
+            boolean done;
+            do {
+                int maxCategory = 1;
+
+                // compute max category for remaining unmapped registers
+                int sz = specs.size();
+                for (int i = 0; i < sz; i++) {
+                    RegisterSpec ssaSpec = specs.get(i);
+                    int category = ssaSpec.getCategory();
+                    if (!ssaRegsMapped.get(ssaSpec.getReg())
+                            && category > maxCategory) {
+                        maxCategory = category;
+                    }
+                }
+
+                ropReg = findRopRegForLocal(ropReg, maxCategory);
+
+                done = tryMapRegs(specs, ropReg, maxCategory, true);
+
+                // Increment for next call to findNext
+                ropReg++;
+            } while (!done);
+        }
+    }
+
+    /**
+     * Tries to map a list of SSA registers into the a rop reg, marking
+     * used rop space as reserved. SSA registers that don't fit are left
+     * unmapped.
+     *
+     * @param specs non-null; SSA registers to attempt to map
+     * @param ropReg &gt;=0 rop register to map to
+     * @param maxAllowedCategory 1 or 2, maximum category allowed in mapping.
+     * @param markReserved do so if true
+     * @return true if all registers wew mapped, false if some remain unmapped.
+     */
+    private boolean tryMapRegs(
+            ArrayList<RegisterSpec> specs, int ropReg,
+            int maxAllowedCategory, boolean markReserved) {
+        boolean remaining = false;
+        for(RegisterSpec spec: specs) {
+            if (ssaRegsMapped.get(spec.getReg())) {
+                continue;
+            }
+
+            boolean succeeded;
+            succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
+            remaining = !succeeded || remaining;
+            if (succeeded && markReserved) {
+                // This only needs to be called once really with
+                // the widest category used, but <shrug>
+                markReserved(ropReg, spec.getCategory());
+            }
+        }
+        return !remaining;
+    }
+
+    /**
+     * Tries to map an SSA register to a rop register.
+     *
+     * @param ssaSpec non-null; SSA register
+     * @param ropReg &gt;=0 rop register
+     * @param maxAllowedCategory 1 or 2, the maximum category that the SSA
+     * register is allowed to be.
+     * @return true if map succeeded, false if not.
+     */
+    private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
+            int maxAllowedCategory) {
+        if (ssaSpec.getCategory() <= maxAllowedCategory
+                && !ssaRegsMapped.get(ssaSpec.getReg())
+                && canMapReg(ssaSpec, ropReg)) {
+            addMapping(ssaSpec, ropReg);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Marks a range of Rop registers as "reserved for a local variable"
+     *
+     * @param ropReg &gt;= 0 rop register to reserve
+     * @param category &gt; 0 width to reserve
+     */
+    private void markReserved(int ropReg, int category) {
+        reservedRopRegs.set(ropReg, ropReg + category, true);
+    }
+
+    /**
+     * Checks to see if any Rop registers in the specified range are reserved
+     * for local variables or parameters
+     *
+     * @param ropRangeStart &gt;= 0 lowest Rop register
+     * @param width &gt; 0 number of Rop registers in range.
+     * @return true if any register in range is marked reserved
+     */
+    private boolean rangeContainsReserved(int ropRangeStart, int width) {
+        for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
+            if (reservedRopRegs.get(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if given rop register represents the "this" pointer
+     * for a non-static method
+     *
+     * @param startReg rop register
+     * @return true if the "this" pointer is located here.
+     */
+    private boolean isThisPointerReg(int startReg) {
+        // "this" is always the first parameter
+        return startReg == 0 && !ssaMeth.isStatic();
+    }
+
+    /**
+     * Finds a range of unreserved Rop registers.
+     *
+     * @param startReg &gt;= 0; a Rop register to start the search at
+     * @param width &gt; 0; the width, in registers, required.
+     * @return &gt;= 0; start of available register range.
+     */
+    private int findNextUnreservedRopReg(int startReg, int width) {
+        if (minimizeRegisters && !isThisPointerReg(startReg)) {
+            return startReg;
+        }
+
+        int reg;
+
+        reg = reservedRopRegs.nextClearBit(startReg);
+
+        while (true) {
+            int i = 1;
+
+            while (i < width && !reservedRopRegs.get(reg + i)) {
+                i++;
+            }
+
+            if (i == width) {
+                return reg;
+            }
+
+            reg = reservedRopRegs.nextClearBit(reg + i);
+        }
+    }
+
+    /**
+     * Finds a range of rop regs that can be used for local variables.
+     * If <code>MIX_LOCALS_AND_OTHER</code> is false, this means any
+     * rop register that has not yet been used.
+     *
+     * @param startReg &gt;= 0; a Rop register to start the search at
+     * @param width &gt; 0; the width, in registers, required.
+     * @return &gt;= 0; start of available register range.
+     */
+    private int findRopRegForLocal(int startReg, int width) {
+        if (minimizeRegisters && !isThisPointerReg(startReg)) {
+            return startReg;
+        }
+
+        int reg;
+
+        reg = usedRopRegs.nextClearBit(startReg);
+
+        while (true) {
+            int i = 1;
+
+            while (i < width && !usedRopRegs.get(reg + i)) {
+                i++;
+            }
+
+            if (i == width) {
+                return reg;
+            }
+
+            reg = usedRopRegs.nextClearBit(reg + i);
+        }
+    }
+
+    /**
+     * Maps any parameter that isn't local-associated, which can happen
+     * in the case where there is no java debug info.
+     */
+    private void handleUnassociatedParameters() {
+        int szSsaRegs = ssaMeth.getRegCount();
+        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+            if (ssaRegsMapped.get(ssaReg)) {
+                // We already did this one above
+                continue;
+            }
+
+            int paramIndex = getParameterIndexForReg(ssaReg);
+
+            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+            if (paramIndex >= 0) {
+                addMapping(ssaSpec, paramIndex);
+            }            
+        }
+    }
+
+    /**
+     * Handles all insns that want a register range for their sources.
+     */
+    private void handleInvokeRangeInsns() {
+        for(NormalSsaInsn insn: invokeRangeInsns) {
+            adjustAndMapSourceRangeRange(insn);
+        }
+    }
+
+    /**
+     * Handles check cast results to reuse the same source register if possible
+     */
+    private void handleCheckCastResults() {
+        for (NormalSsaInsn insn : moveResultPseudoInsns) {
+            RegisterSpec moveRegSpec = insn.getResult();
+            int moveReg = moveRegSpec.getReg();
+            BitSet predBlocks = insn.getBlock().getPredecessors();
+
+            // Expect one predecessor block only
+            if (predBlocks.cardinality() != 1) {
+                continue;
+            }
+
+            SsaBasicBlock predBlock =
+                    ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
+            ArrayList<SsaInsn> insnList = predBlock.getInsns();
+
+            /**
+             * If the predecessor block has a check-cast, it will be the last
+             * instruction
+             */
+            SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
+            if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
+                continue;
+            }
+
+            RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
+            int checkReg = checkRegSpec.getReg();
+
+            // Assume none of the register is mapped yet
+            int ropReg = 0;
+
+            /**
+             * See if either register is already mapped. Most likely the move
+             * result will be mapped already since the cast result is stored
+             * in a local variable.
+             */
+            if (ssaRegsMapped.get(moveReg)) {
+                ropReg = mapper.oldToNew(moveReg);
+            } else if (ssaRegsMapped.get(checkReg)) {
+                ropReg = mapper.oldToNew(checkReg);
+            }
+
+            ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(2);
+            ssaRegs.add(moveRegSpec);
+            ssaRegs.add(checkRegSpec);
+            int category = checkRegSpec.getCategory();
+
+            while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
+                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+            }
+        }
+    }
+
+    /**
+     * Maps all non-parameter, non-local variable
+     * registers.
+     */
+    private void handleNormalUnassociated() {
+        int szSsaRegs = ssaMeth.getRegCount();
+        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+            if (ssaRegsMapped.get(ssaReg)) {
+                // We already did this one
+                continue;
+            }
+
+            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+
+            if (ssaSpec == null) continue;
+
+            int category = ssaSpec.getCategory();
+            // Find a rop reg that does not interfere
+            int ropReg = findNextUnreservedRopReg(0, category);
+            while (!canMapReg(ssaSpec, ropReg)) {
+                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+            }
+
+            addMapping(ssaSpec, ropReg);
+        }
+    }
+
+    /**
+     * Checks to see if <code>ssaSpec</code> can be mapped to
+     * <code>ropReg</code>. Checks interference graph and ensures
+     * the range does not cross the parameter range.
+     *
+     * @param ssaSpec non-null; SSA spec
+     * @param ropReg prosepctive new-namespace reg
+     * @return true if mapping is possible
+     */
+    private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
+        int category = ssaSpec.getCategory();
+        return !(spansParamRange(ropReg, category)
+                || mapper.interferes(ssaSpec, ropReg));
+    }
+ 
+    /**
+     * Returns true if the specified Rop register + category
+     * will cross the boundry between the lower <code>paramWidth</code>
+     * registers reserved for method params and the upper registers. We cannot
+     * allocate a register that spans the param block and the normal block,
+     * because we will be moving the param block to high registers later.
+     * 
+     * @param ssaReg register in new namespace
+     * @param category width that the register will have
+     * @return true in the case noted above.
+     */
+    private boolean spansParamRange(int ssaReg, int category) {
+        return ((ssaReg < paramRangeEnd)
+                && ((ssaReg + category) > paramRangeEnd));
+    }
+
+    /**
+     * Analyze each instruction and find out all the local variable assignments
+     * and move-result-pseudo/invoke-range instrucitons.
+     */
+    private void analyzeInstructions() {
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+
+            /** {@inheritDoc} */
+            public void visitMoveInsn(NormalSsaInsn insn) {
+                processInsn(insn);
+            }
+
+            /** {@inheritDoc} */
+            public void visitPhiInsn(PhiInsn insn) {
+                processInsn(insn);
+            }
+
+            /** {@inheritDoc} */
+            public void visitNonMoveInsn(NormalSsaInsn insn) {
+                processInsn(insn);
+            }
+
+            /**
+             * This method collects three types of instructions:
+             * 1) Adds a local variable assignment to the
+             *    <code>localVariables</code> map.
+             * 2) Add move-result-pseudo to the
+             *    <code>moveResultPseudoInsns</code> list.
+             * 3) Add invoke-range to the
+             *    <code>invokeRangeInsns</code> list.
+             *
+             * @param insn non-null; insn that may represent a local variable
+             * assignment.
+             */
+            private void processInsn(SsaInsn insn) {
+                RegisterSpec assignment;
+                assignment = insn.getLocalAssignment();
+
+                if (assignment != null) {
+                    LocalItem local = assignment.getLocalItem();
+
+                    ArrayList<RegisterSpec> regList = localVariables.get(local);
+
+                    if (regList == null) {
+                        regList = new ArrayList<RegisterSpec>();
+                        localVariables.put(local, regList);
+                    }
+
+                    regList.add(assignment);
+                }
+
+                if (insn instanceof NormalSsaInsn) {
+                    if (insn.getOpcode().getOpcode() ==
+                            RegOps.MOVE_RESULT_PSEUDO) {
+                        moveResultPseudoInsns.add((NormalSsaInsn) insn);
+                    } else if (Optimizer.getAdvice().requiresSourcesInOrder(
+                            insn.getOriginalRopInsn().getOpcode(),
+                            insn.getSources())) {
+                        invokeRangeInsns.add((NormalSsaInsn) insn);
+                    }
+                }
+
+            }
+        });
+    }
+
+    /**
+     * Adds a mapping from an SSA register to a Rop register. <code>
+     * canMapReg</code> should have already been called.
+     *
+     * @param ssaSpec non-null; SSA register to map from
+     * @param ropReg &gt;=0; Rop register to map to
+     */
+    private void addMapping(RegisterSpec ssaSpec, int ropReg) {
+        int ssaReg = ssaSpec.getReg();
+
+        // An assertion
+        if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
+            throw new RuntimeException(
+                    "attempt to add invalid register mapping");
+        }
+
+        if (DEBUG) {
+            System.out.printf("Add mapping s%d -> v%d c:%d\n",
+                    ssaSpec.getReg(), ropReg, ssaSpec.getCategory());               
+
+        }
+
+        int category = ssaSpec.getCategory();
+        mapper.addMapping(ssaSpec.getReg(), ropReg, category);
+        ssaRegsMapped.set(ssaReg);
+        usedRopRegs.set(ropReg, ropReg + category);
+    }
+
+
+    /**
+     * Maps the source registers of the specified instruction such that they
+     * will fall in a contiguous range in Rop form. Moves are inserted as
+     * necessary to allow the range to be allocated.
+     *
+     * @param insn non-null; insn whos sources to process
+     */
+    private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
+        int newRegStart;
+
+        newRegStart = findRangeAndAdjust(insn);
+
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        int nextRopReg = newRegStart;
+        for (int i = 0; i < szSources; i++) {
+            RegisterSpec source = sources.get(i);
+            int sourceReg = source.getReg();
+            int category = source.getCategory();
+            int curRopReg = nextRopReg;
+            nextRopReg += category;
+
+            if (ssaRegsMapped.get(sourceReg)) {
+                continue;
+            }
+
+            LocalItem localItem = getLocalItemForReg(sourceReg);
+            addMapping(source, curRopReg);
+
+            if (localItem != null) {
+                markReserved(curRopReg, category);
+                ArrayList<RegisterSpec> similarRegisters
+                        = localVariables.get(localItem);
+
+                int szSimilar = similarRegisters.size();
+
+                // Try to map all SSA registers also associated with this local
+                for (int j = 0; j < szSimilar; j++) {
+                    RegisterSpec similarSpec = similarRegisters.get(j);
+                    int similarReg = similarSpec.getReg();
+
+                    // ...and don't map anything that's also a source...
+                    if (-1 != sources.indexOfRegister(similarReg)) {
+                        continue;
+                    }
+
+                    // Registers left unmapped will get handled later
+                    tryMapReg(similarSpec, curRopReg, category);
+                }
+            }
+        }
+    }
+
+    /**
+     * Find a contiguous rop register range that fits the specified
+     * instruction's sources. First, try to center the range around
+     * sources that have already been mapped to Rop registers. If that fails,
+     * just find a new contiguous range that doesn't interfere.
+
+     * @param insn non-null; the insn whose sources need to fit. Must be
+     * last insn in basic block.
+     * @return &gt;= 0 rop register of start of range
+     */
+    private int findRangeAndAdjust(NormalSsaInsn insn) {
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        // the category for each source index
+        int categoriesForIndex[] = new int[szSources];
+        int rangeLength = 0;
+
+        // Compute rangeLength and categoriesForIndex
+        for (int i = 0; i < szSources; i++) {
+            int category = sources.get(i).getCategory();
+            categoriesForIndex[i] = category;
+            rangeLength += categoriesForIndex[i];
+        }
+
+        // The highest score of fits tried so far
+        int maxScore = Integer.MIN_VALUE;
+        // the high scoring range's start
+        int resultRangeStart = -1;
+        // by source index: set of sources needing moves in high scoring plan
+        BitSet resultMovesRequired = null;
+
+        /*
+         * First, go through each source that's already been mapped. Try
+         * to center the range around the Rop register this source is mapped
+         * to.
+         */
+        int rangeStartOffset = 0;
+        for (int i = 0; i < szSources; i++) {
+            int ssaCenterReg = sources.get(i).getReg();
+
+            if (i != 0) {
+                rangeStartOffset -= categoriesForIndex[i - 1];
+            }
+            if (!ssaRegsMapped.get(ssaCenterReg)) {
+                continue;
+            }
+
+            int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
+
+            if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
+                continue;
+            }
+
+            BitSet curMovesRequired = new BitSet(szSources);
+
+            int fitWidth
+                    = fitPlanForRange(rangeStart, insn, categoriesForIndex,
+                    curMovesRequired);
+
+            if (fitWidth < 0) {
+                continue;
+            }
+
+            int score = fitWidth - curMovesRequired.cardinality();
+
+            if (score > maxScore) {
+                maxScore = score;
+                resultRangeStart = rangeStart;
+                resultMovesRequired = curMovesRequired;
+            }
+
+            if (fitWidth == rangeLength) {
+                // We can't do any better than this, so stop here
+                break;
+            }
+        }
+
+        /*
+         * If we were unable to find a plan for a fit centered around
+         * an already-mapped source, just try to find a range of
+         * registers we can move the range into.
+         */
+
+        if (resultRangeStart == -1) {
+            resultMovesRequired = new BitSet(szSources);
+
+            resultRangeStart = findAnyFittingRange(insn, rangeLength,
+                    categoriesForIndex, resultMovesRequired);
+        }
+
+        /*
+         * Now, insert any moves required
+         */
+
+        for (int i = resultMovesRequired.nextSetBit(0); i >= 0
+                ; i = resultMovesRequired.nextSetBit(i+1)) {
+            insn.changeOneSource(i,
+                    insertMoveBefore(insn, sources.get(i)));
+        }
+
+        return resultRangeStart;
+    }
+
+    /**
+     * Finds an unreserved range that will fit the sources of the
+     * specified instruction. Does not bother trying to center the range
+     * around an already-mapped source register;
+     *
+     * @param insn non-null; insn to build range for
+     * @param rangeLength &gt;=0 length required in register units.
+     * @param categoriesForIndex non-null; indexed by source index;
+     * the category for each source.
+     * @param outMovesRequired non-null; an output parameter indexed by
+     * source index that will contain the set of sources which need
+     * moves inserted.
+     * @return the rop register that starts the fitting range.
+     */
+    private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
+            int[] categoriesForIndex, BitSet outMovesRequired) {
+        int rangeStart = 0;
+        while (true) {
+            rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength);
+            int fitWidth
+                    = fitPlanForRange(rangeStart, insn,
+                    categoriesForIndex, outMovesRequired);
+
+            if (fitWidth >= 0) {
+                break;
+            }
+            rangeStart++;
+            outMovesRequired.clear();
+        }
+        return rangeStart;
+    }
+
+    /**
+     * Attempts to build a plan for fitting a range of sources into rop
+     * registers.
+     *
+     * @param ropReg &gt;=0 rop reg that begins range
+     * @param insn non-null; insn to plan range for
+     * @param categoriesForIndex non-null; indexed by source index;
+     * the category for each source.
+     * @param outMovesRequired non-null; an output parameter indexed by
+     * source index that will contain the set of sources which need
+     * moves inserted.
+     * @return the width of the fit that that does not involve added moves or
+     * -1 if "no fit possible"
+     */
+    private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
+            int[] categoriesForIndex, BitSet outMovesRequired) {
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        int fitWidth = 0;
+        IntSet liveOut = insn.getBlock().getLiveOutRegs();
+        RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
+
+        // An SSA reg may only be mapped into a range once
+        BitSet seen = new BitSet(ssaMeth.getRegCount());
+
+        for (int i = 0; i < szSources ; i++) {
+            RegisterSpec ssaSpec = sources.get(i);
+            int ssaReg = ssaSpec.getReg();
+            int category = categoriesForIndex[i];
+
+            if (i != 0) {
+                ropReg += categoriesForIndex[i-1];
+            }
+
+            if (ssaRegsMapped.get(ssaReg)
+                    && mapper.oldToNew(ssaReg) == ropReg) {
+                // A register already mapped appropriately
+                fitWidth += category;
+            } else if (rangeContainsReserved(ropReg, category)) {
+                fitWidth = -1;
+                break;
+            } else if (!ssaRegsMapped.get(ssaReg)
+                    && canMapReg(ssaSpec, ropReg)
+                    && !seen.get(ssaReg)) {
+                // A register that can be mapped appropriately
+                fitWidth += category;
+            } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
+                    && !mapper.areAnyPinned(sources, ropReg, category)) {
+                /*
+                 * A source that can be moved
+                 * We can insert a move as long as:
+                 *
+                 *  - no SSA register pinned to the desired rop reg
+                 * is live out on the block
+                 *  - no SSA register pinned to desired rop reg is
+                 *  a source of this insn (since this may require
+                 * overlapping moves, which we can't presently handle)
+                 */
+
+                outMovesRequired.set(i);
+            } else {
+                fitWidth = -1;
+                break;
+            }
+
+            seen.set(ssaReg);
+        }
+        return fitWidth;
+    }
+
+    /**
+     * Converts a bit set of SSA registers into a RegisterSpecList containing
+     * the definition specs of all the registers.
+     *
+     * @param ssaSet non-null; set of SSA registers
+     * @return list of RegisterSpecs as noted above
+     */
+    RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
+        RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
+
+        IntIterator iter = ssaSet.iterator();
+
+        int i = 0;
+        while (iter.hasNext()) {
+            result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
+        }
+        
+        return result;
+    }
+
+    /**
+     * Gets a local item associated with an ssa register, if one exists
+     *
+     * @param ssaReg &gt;= 0 SSA register
+     * @return null-ok; associated local item or null
+     */
+    private LocalItem getLocalItemForReg(int ssaReg) {
+        for(Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry:
+                localVariables.entrySet()) {
+
+            for (RegisterSpec spec: entry.getValue()) {
+                if (spec.getReg() == ssaReg) {
+                    return entry.getKey();
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
new file mode 100644
index 0000000..abc5fca
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.util.IntList;
+
+import java.util.BitSet;
+
+/**
+ * Searches for basic blocks that all have the same successor and insns
+ * but different predecessors. These blocks are then combined into a single
+ * block and the now-unused blocks are deleted. These identical blocks
+ * frequently are created when catch blocks are edge-split.
+ */
+public class IdenticalBlockCombiner {
+
+    private RopMethod ropMethod;
+    private BasicBlockList blocks;
+    private BasicBlockList newBlocks;
+
+    /**
+     * Constructs instance. Call <code>process()</code> to run.
+     * @param rm instance to process
+     */
+    public IdenticalBlockCombiner(RopMethod rm) {
+        ropMethod = rm;
+        blocks = ropMethod.getBlocks();
+        newBlocks = blocks.getMutableCopy();
+    }
+
+    /**
+     * Runs algorithm.  TODO: this is n^2, and could be made linear-ish with
+     * a hash.
+     *
+     * @return new method that has been processed
+     */
+    public RopMethod process() {
+        int szBlocks = blocks.size();
+        // indexed by label
+        BitSet toDelete = new BitSet(blocks.getMaxLabel());
+
+        // For each non-deleted block...
+        for (int bindex = 0; bindex < szBlocks; bindex++) {
+            BasicBlock b = blocks.get(bindex);
+
+            if (toDelete.get(b.getLabel())) {
+                // doomed block
+                continue;
+            }
+
+            IntList preds = ropMethod.labelToPredecessors(b.getLabel());
+
+            // ...look at all of it's predecessors that have only one succ...
+            int szPreds = preds.size();
+            for (int i = 0; i < szPreds; i++) {
+                int iLabel = preds.get(i);
+
+                BasicBlock iBlock = blocks.labelToBlock(iLabel);
+
+                if (toDelete.get(iLabel) || iBlock.getSuccessors().size() > 1) {
+                    continue;
+                }
+
+                IntList toCombine = new IntList();
+
+                // ...and see if they can be combined with any other preds...
+                for (int j = i + 1; j <szPreds; j++) {
+                    int jLabel = preds.get(j);
+                    BasicBlock jBlock = blocks.labelToBlock(jLabel);
+
+                    if (jBlock.getSuccessors().size() == 1
+                            && compareInsns(iBlock, jBlock)) {
+
+                        toCombine.add(jLabel);
+                        toDelete.set(jLabel);
+                    }
+                }
+
+                combineBlocks(iLabel, toCombine);
+            }
+        }
+
+        for (int i = szBlocks - 1; i > 0; i--) {
+            if (toDelete.get(newBlocks.get(i).getLabel())) {
+                newBlocks.set(i, null);
+            }
+        }
+
+        newBlocks.shrinkToFit();
+        newBlocks.setImmutable();
+
+        return new RopMethod(newBlocks, ropMethod.getFirstLabel());
+    }
+
+    private boolean compareInsns(BasicBlock a, BasicBlock b) {
+        return a.getInsns().contentEquals(b.getInsns());
+    }
+
+    /**
+     * Combines blocks proven identical into one alpha block, re-writing
+     * all of the successor links that point to the beta blocks to point
+     * to the alpha block instead.
+     *
+     * @param alphaLabel block that will replace all the beta block
+     * @param betaLabels label list of blocks to combine
+     */
+    private void combineBlocks(int alphaLabel, IntList betaLabels) {
+        int szBetas = betaLabels.size();
+
+        for (int i = 0; i < szBetas; i++) {
+            int betaLabel = betaLabels.get(i);
+            BasicBlock bb = blocks.labelToBlock(betaLabel);
+
+            IntList preds;
+
+            preds = ropMethod.labelToPredecessors(bb.getLabel());
+
+            int szPreds = preds.size();
+
+            for (int j = 0; j < szPreds; j++) {
+                BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j));
+                replaceSucc(predBlock, betaLabel, alphaLabel);
+            }
+        }
+    }
+
+    /**
+     * Replaces one of a block's successors with a different label. Constructs
+     * an updated BasicBlock instance and places it in <code>newBlocks</code>.
+     *
+     * @param block block to replace
+     * @param oldLabel label of successor to replace
+     * @param newLabel label of new successor
+     */
+    private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) {
+
+        IntList newSuccessors = block.getSuccessors().mutableCopy();
+        int newPrimarySuccessor;
+
+        newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel);
+        newPrimarySuccessor = block.getPrimarySuccessor();
+        if (newPrimarySuccessor == oldLabel) {
+            newPrimarySuccessor = newLabel;
+        }
+
+        newSuccessors.setImmutable();
+
+        BasicBlock newBB = new BasicBlock(block.getLabel(),
+                block.getInsns(), newSuccessors, newPrimarySuccessor);
+
+        newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
new file mode 100644
index 0000000..282420b
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.SetFactory;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A register interference graph
+ */
+public class InterferenceGraph {
+    /** interference graph, indexed by register in both dimensions  */
+    private final ArrayList<IntSet> interference;
+
+    /**
+     * Creates a new graph.
+     *
+     * @param countRegs &gt;=0 the start count of registers in the namespace.
+     * New registers can be added subsequently.
+     */
+    public InterferenceGraph(int countRegs) {
+        interference = new ArrayList<IntSet>(countRegs);
+
+        for (int i = 0; i < countRegs; i++) {
+            interference.add(SetFactory.makeInterferenceSet(countRegs));
+        }
+    }
+
+    /**
+     * Adds a register pair to the interference/liveness graph. Parameter
+     * order is insignificant.
+     *
+     * @param regV one register index
+     * @param regW another register index
+     */
+    public void add(int regV, int regW) {
+        ensureCapacity(Math.max(regV, regW) + 1);
+
+        interference.get(regV).add(regW);
+        interference.get(regW).add(regV);
+    }
+
+    /**
+     * Dumps interference graph to stdout for debugging.
+     */
+    public void dumpToStdout() {
+        int oldRegCount = interference.size();
+
+        for (int i = 0; i < oldRegCount; i++) {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("Reg " + i + ":" + interference.get(i).toString());
+
+            System.out.println(sb.toString());
+        }
+    }
+
+    /**
+     * Merges the interference set for a register into a given bit set
+     *
+     * @param reg &gt;=0 register
+     * @param set non-null; interference set; will be merged with set for
+     * given register
+     */
+    public void mergeInterferenceSet(int reg, IntSet set) {
+        if (reg < interference.size()) {
+            set.merge(interference.get(reg));
+        }
+    }
+
+    /**
+     * Ensures that the interference graph is appropriately sized.
+     *
+     * @param size requested minumum size
+     */
+    private void ensureCapacity(int size) {
+        int countRegs = interference.size();
+        interference.ensureCapacity(size);
+        for (int i = countRegs ; i < size; i++) {
+            interference.add(SetFactory.makeInterferenceSet(size));
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
new file mode 100644
index 0000000..5ae6e07
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * From Appel "Modern Compiler Implementation in Java" algorithm 19.17
+ * Calculate the live ranges for register <code>reg</code>.<p>
+ *
+ * v = regV <p>
+ * s = insn <p>
+ * M = visitedBlocks <p>
+ */
+public class LivenessAnalyzer {
+
+    /**
+     * non-null; index by basic block indexed set of basic blocks
+     * that have already been visited. "M" as written in the original Appel
+     * algorithm.
+     */
+    private final BitSet visitedBlocks;
+
+    /**
+     * non-null; set of blocks remaing to visit as "live out as block"
+     */
+    private final BitSet liveOutBlocks;
+
+    /**
+     * &gt;=0; SSA register currently being analyzed.
+     * "v" in the original Appel algorithm.
+     */
+    private final int regV;
+
+    /** method to process */
+    private final SsaMethod ssaMeth;
+
+    /** interference graph being updated */
+    private final InterferenceGraph interference;
+
+    /** block "n" in Appel 19.17 */
+    SsaBasicBlock blockN;
+
+    /** index of statement <code>s</code> in <code>blockN</code>*/
+    private int statementIndex;
+
+    /** the next function to call. one of the four constants below */
+    private int nextFunction;
+
+    /** constants for nextFunction */
+    static final int LIVE_IN_AT_STATEMENT = 1;
+    static final int LIVE_OUT_AT_STATEMENT = 2;
+    static final int LIVE_OUT_AT_BLOCK = 3;
+    static final int DONE = 4;
+
+    /**
+     * Runs register liveness algorithm for a method, updating the
+     * live in/out information in <code>SsaBasicBlock</code> instances and
+     * returning an interference graph.
+     *
+     * @param ssaMeth non-null; Method to process.
+     * @return non-null; interference graph indexed by SSA registers in both
+     * directions.
+     */
+    public static InterferenceGraph constructInterferenceGraph(
+            SsaMethod ssaMeth) {
+        int szRegs = ssaMeth.getRegCount();
+
+        InterferenceGraph interference = new InterferenceGraph(szRegs);
+
+        for (int i = 0; i < szRegs; i++) {
+            new LivenessAnalyzer(ssaMeth, i, interference).run();
+        }
+
+        coInterferePhis(ssaMeth, interference);
+
+        return interference;
+    }
+    
+    /**
+     * Makes liveness analyzer instance for specific register.
+     *
+     * @param ssaMeth non-null; method to process
+     * @param reg register whose liveness to analyze
+     * @param interference non-null; indexed by SSA reg in both dimensions;
+     * graph to update
+     *
+     */
+    private LivenessAnalyzer(final SsaMethod ssaMeth, final int reg,
+            InterferenceGraph interference) {
+        this.ssaMeth = ssaMeth;
+        this.regV = reg;
+        visitedBlocks = new BitSet(ssaMeth.getBlocks().size());
+        liveOutBlocks = new BitSet(ssaMeth.getBlocks().size());
+        this.interference = interference;
+    }
+
+    /**
+     * The algorithm in Appel is presented in
+     * partial tail-recursion form. Obviously, that's not
+     * efficient in java, so this function serves
+     * as the dispatcher instead.
+     */
+    private void handleTailRecursion() {
+        while (nextFunction != DONE) {
+            switch (nextFunction) {
+                case LIVE_IN_AT_STATEMENT:
+                    nextFunction = DONE;
+                    liveInAtStatement();
+                    break;
+
+                case LIVE_OUT_AT_STATEMENT:
+                    nextFunction = DONE;
+                    liveOutAtStatement();
+                    break;
+
+                case LIVE_OUT_AT_BLOCK:
+                    nextFunction = DONE;
+                    liveOutAtBlock();
+                    break;
+
+                default:
+            }
+        }
+    }
+
+    /**
+     * From Appel algorithm 19.17
+     */
+    public void run() {
+        List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV);
+
+        for (SsaInsn insn: useList) {
+            nextFunction = DONE;
+
+            if (insn instanceof PhiInsn) {
+                // If s is a phi-function with V as it's ith argument
+                PhiInsn phi = (PhiInsn) insn;
+
+                for (SsaBasicBlock pred: phi.predBlocksForReg(regV, ssaMeth)) {
+
+                    blockN = pred;
+
+                    nextFunction = LIVE_OUT_AT_BLOCK;
+                    handleTailRecursion();
+                }
+            } else {
+                blockN = insn.getBlock();
+                statementIndex = blockN.getInsns().indexOf(insn);
+
+                if (statementIndex < 0) {
+                    throw new RuntimeException(
+                            "insn not found in it's own block");
+                }
+
+                nextFunction = LIVE_IN_AT_STATEMENT;
+                handleTailRecursion();
+            }
+        }
+
+        int nextLiveOutBlock;
+        while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) {
+            blockN = ssaMeth.getBlocks().get(nextLiveOutBlock);
+            liveOutBlocks.clear(nextLiveOutBlock);
+            nextFunction = LIVE_OUT_AT_BLOCK;
+            handleTailRecursion();
+        }
+    }
+
+    /**
+     * "v is live-out at n"
+     */
+    private void liveOutAtBlock() {
+        if (! visitedBlocks.get(blockN.getIndex())) {
+            visitedBlocks.set(blockN.getIndex());
+
+            blockN.addLiveOut(regV);
+
+            ArrayList<SsaInsn> insns;
+
+            insns = blockN.getInsns();
+
+            // Live out at last statement in blockN
+            statementIndex = insns.size() - 1;
+            nextFunction = LIVE_OUT_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * "v is live-in at s"
+     */
+    private void liveInAtStatement() {
+
+        // if s is the first statement in block N
+        if (statementIndex == 0) {
+            // v is live-in at n
+            blockN.addLiveIn(regV);
+
+            BitSet preds = blockN.getPredecessors();
+
+            liveOutBlocks.or(preds);
+        } else {
+            // Let s' be the statement preceeding s
+            statementIndex -= 1;
+            nextFunction = LIVE_OUT_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * "v is live-out at s"
+     */
+    private void liveOutAtStatement() {
+
+        SsaInsn statement = blockN.getInsns().get(statementIndex);
+        RegisterSpec rs = statement.getResult();
+
+        if (!statement.isResultReg(regV)) {
+            if(rs != null) {
+                interference.add(regV, rs.getReg());
+            }
+            nextFunction = LIVE_IN_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * Ensures that all the phi result registers for all the phis in the
+     * same basic block interfere with each other. This is needed since
+     * the dead code remover has allowed through "dead-end phis" whose
+     * results are not used except as local assignments. Without this step,
+     * a the result of a dead-end phi might be assigned the same register
+     * as the result of another phi, and the phi removal move scheduler may
+     * generate moves that over-write the live result.
+     *
+     * @param ssaMeth non-null; method to pricess
+     * @param interference non-null; interference graph
+     */
+    private static void coInterferePhis(SsaMethod ssaMeth,
+            InterferenceGraph interference) {
+        for (SsaBasicBlock b: ssaMeth.getBlocks()) {
+            List<SsaInsn> phis = b.getPhiInsns();
+
+            int szPhis = phis.size();
+
+            for (int i = 0; i < szPhis; i++) {
+                for (int j = 0; j < szPhis; j++) {
+                    if (i == j) {
+                        continue;
+                    }
+
+                    interference.add(phis.get(i).getResult().getReg(),
+                        phis.get(j).getResult().getReg());
+                }
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
new file mode 100644
index 0000000..cd3b2f1
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * A register allocator that maps SSA register n to Rop register 2*n,
+ * essentially preserving the original mapping and remaining agnostic
+ * about normal or wide categories. Used for debugging.
+ */
+public class NullRegisterAllocator extends RegisterAllocator {
+
+    /** {@inheritDoc} */
+    public NullRegisterAllocator(
+            final SsaMethod ssaMeth, final InterferenceGraph interference) {
+
+        super(ssaMeth, interference);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        // We're not smart enough for this.
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+        int oldRegCount = ssaMeth.getRegCount();
+
+        BasicRegisterMapper mapper
+                = new BasicRegisterMapper(oldRegCount);
+
+        for (int i = 0; i < oldRegCount; i++) {
+            mapper.addMapping(i, i*2, 2);
+        }
+
+        return mapper;
+    }    
+}
diff --git a/dx/src/com/android/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
new file mode 100644
index 0000000..764b03a
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Base class of all register allocators
+ */
+public abstract class RegisterAllocator {
+
+    /** method being processed */
+    protected final SsaMethod ssaMeth;
+
+    /** interference graph, indexed by register in both dimensions */
+    protected final InterferenceGraph interference;
+
+    /**
+     * Creates an instance. Call <code>allocateRegisters</code> to run.
+     * @param ssaMeth method to process.
+     * @param interference Interference graph, indexed by register in both
+     * dimensions.
+     */
+    public RegisterAllocator(
+            final SsaMethod ssaMeth, final InterferenceGraph interference) {
+        this.ssaMeth = ssaMeth;
+        this.interference = interference;
+    }
+
+    /**
+     * Indicates whether the method params were allocated at the bottom
+     * of the namespace, and thus should be moved up to the top of the
+     * namespace after phi removal.
+     *
+     * @return true if params should be moved from low to high.
+     */
+    public abstract boolean wantsParamsMovedHigh();
+
+    /**
+     * Runs the algorithm.
+     * @return a register mapper to apply to the <code>SsaMethod</code>
+     */
+    public abstract RegisterMapper allocateRegisters();
+
+    /**
+     * Returns the category (width) of the definition site of the register.
+     * Returns 1 for undefined registers.
+     *
+     * @param reg register
+     * @return 1 or 2
+     */
+    protected int getCategoryForSsaReg(int reg) {
+        SsaInsn definition;
+        definition = ssaMeth.getDefinitionForRegister(reg);
+
+        if (definition == null) {
+            // an undefined reg
+            return 1;
+        } else {
+            return definition.getResult().getCategory();
+        }
+    }
+
+    /**
+     * Returns the RegisterSpec of the definition of the register.
+     *
+     * @param reg &gt;= 0 SSA register
+     * @return definition spec of the register or null if it is never defined
+     * (for the case of "version 0" SSA registers).
+     */
+    protected RegisterSpec getDefinitionSpecForSsaReg(int reg) {
+        SsaInsn definition;
+        definition = ssaMeth.getDefinitionForRegister(reg);
+
+        return definition == null ? null : definition.getResult();
+    }
+
+    /**
+     * Returns true if the definition site of this register is a
+     * move-param (ie, this is a method parameter)
+     * @param reg register in question
+     * @return true if this is a method parameter
+     */
+    protected boolean isDefinitionMoveParam(int reg) {
+        SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg);
+        if (defInsn instanceof NormalSsaInsn) {
+            NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn;
+
+            return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM;
+        }
+
+        return false;
+    }
+
+    /**
+     * Inserts a move instruction for a specified SSA register before a
+     * specified instruction, creating a new SSA register and adjusting the
+     * interference graph in the process. The insn currently must be the
+     * last insn in a block.
+     *
+     * @param insn non-null; insn to insert move before, must be last insn
+     * in block.
+     * @param reg non-null; SSA register to duplicate
+     * @return non-null; spec of new SSA register created by move
+     */
+    protected final RegisterSpec insertMoveBefore(SsaInsn insn,
+            RegisterSpec reg) {
+
+        SsaBasicBlock block = insn.getBlock();
+        ArrayList<SsaInsn> insns = block.getInsns();
+        int insnIndex = insns.indexOf(insn);
+
+        if (insnIndex < 0 ) {
+            throw new IllegalArgumentException (
+                    "specified insn is not in this block");
+        }
+
+        if (insnIndex != insns.size() - 1) {
+            /*
+             * Presently, the interference updater only works when
+             * adding before the last insn, and the last insn must have no
+             * result
+             */
+            throw new IllegalArgumentException(
+                    "Adding move here not supported:" + insn.toHuman());
+        }
+
+        /*
+         * Get new register and make new move instruction
+         */
+
+        // new result must not have associated local variable
+        RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(),
+                reg.getTypeBearer());
+
+        SsaInsn toAdd;
+
+        toAdd = SsaInsn.makeFromRop(
+                    new PlainInsn(Rops.opMove(newRegSpec.getType()),
+                            SourcePosition.NO_INFO, newRegSpec,
+                            RegisterSpecList.make(reg)), block);
+
+        insns.add(insnIndex, toAdd);
+
+        int newReg = newRegSpec.getReg();
+
+        /*
+         * Adjust interference graph based on what's live out of the current
+         * block and what's used by the final instruction.
+         */
+
+        IntSet liveOut = block.getLiveOutRegs();
+
+        RegisterSpec result = insn.getResult();
+        int resultReg = (result == null) ? -1 : result.getReg();
+
+        IntIterator liveOutIter = liveOut.iterator();
+
+        while(liveOutIter.hasNext()) {
+            interference.add(newReg, liveOutIter.next());
+        }
+
+        // Everything that's a source in the last insn interferes
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+
+        for (int i = 0; i < szSources; i++) {
+            interference.add(newReg, sources.get(i).getReg());
+        }
+
+        ssaMeth.onInsnsChanged();
+
+        return newRegSpec;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/SsaToRop.java b/dx/src/com/android/dx/ssa/back/SsaToRop.java
new file mode 100644
index 0000000..1c59549
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/SsaToRop.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntList;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Converts a method in SSA form to ROP form.
+ */
+public class SsaToRop {
+
+    private static final boolean DEBUG = false;
+
+    /** non-null; method to process */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * true if the converter should attempt to minimize
+     * the rop-form register count
+     */
+    private final boolean minimizeRegisters;
+
+    /** interference graph */
+    private InterferenceGraph interference;
+
+    /**
+     * Converts a method in SSA form to ROP form.
+     * @param ssaMeth input
+     * @return non-null; rop-form output
+     */
+    public static RopMethod convertToRopMethod(SsaMethod ssaMeth,
+            boolean minimizeRegisters) {
+        return new SsaToRop(ssaMeth, minimizeRegisters).convert();
+    }
+
+    private SsaToRop(final SsaMethod ssaMethod, boolean minimizeRegisters) {
+        this.minimizeRegisters = minimizeRegisters;
+        this.ssaMeth = ssaMethod;
+    }
+
+    private RopMethod convert() {
+        interference = LivenessAnalyzer.constructInterferenceGraph(ssaMeth);
+
+        if (DEBUG) {
+            interference.dumpToStdout();
+        }
+
+        RegisterAllocator allocator;
+        RegisterMapper mapper;
+
+        // These are other allocators for debugging or historical comparison
+
+        //allocator = new NullRegisterAllocator(ssaMeth, interference);
+        //allocator = new FirstFitAllocator(ssaMeth, interference);
+
+        allocator = new FirstFitLocalCombiningAllocator(ssaMeth, interference,
+                minimizeRegisters);
+
+        mapper = allocator.allocateRegisters();
+
+        if (DEBUG) {
+            System.out.println("Printing reg map");
+            System.out.println(((BasicRegisterMapper)mapper).toHuman());
+        }        
+
+        ssaMeth.setBackMode();
+
+        ssaMeth.mapRegisters(mapper);
+
+        removePhiFunctions();
+
+        if (allocator.wantsParamsMovedHigh()) {
+            moveParametersToHighRegisters();
+        }
+
+        removeEmptyGotos();
+
+        RopMethod ropMethod;
+
+        ropMethod = convertToRop();
+
+        ropMethod = new IdenticalBlockCombiner(ropMethod).process();
+
+        return ropMethod;
+    }
+
+
+    /**
+     * Removes all blocks containing only GOTOs from the control flow. Although
+     * much of this work will be done later when converting from rop to dex,
+     * not all simplification cases can be handled there. Furthermore, any no-op
+     * block between the exit block and blocks containing the real return or
+     * throw statements must be removed.
+     */
+    private void removeEmptyGotos() {
+        final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() {
+            public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
+                ArrayList<SsaInsn> insns = b.getInsns();
+
+                if ((insns.size() == 1)
+                        && (insns.get(0).getOpcode() == Rops.GOTO)) {
+
+                    BitSet preds = (BitSet)b.getPredecessors().clone();
+
+                    for (int i = preds.nextSetBit(0); i >= 0;
+                            i = preds.nextSetBit(i + 1)) {
+                        SsaBasicBlock pb = blocks.get(i);
+                        pb.replaceSuccessor(b.getIndex(),
+                                b.getPrimarySuccessorIndex());
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * This method is not presently used.
+     * @return a list of registers ordered by most-frequently-used
+     * to least-frequently-used. Each register is listed once and only once.
+     */
+    public int[] getRegistersByFrequency() {
+        int regCount = ssaMeth.getRegCount();
+        Integer[] ret = new Integer[ssaMeth.getRegCount()];
+
+        for (int i = 0; i < regCount; i++) {
+            ret[i] = i;
+        }
+
+        java.util.Arrays.sort(ret, new java.util.Comparator<Integer>() {
+            public int compare (Integer o1, Integer o2) {
+                return ssaMeth.getUseListForRegister(o2).size()
+                        - ssaMeth.getUseListForRegister(o1).size();
+            }
+
+            public boolean equals(Object o) {
+                return o == this;
+            }
+        });
+
+        int result[] = new int[regCount];
+
+        for (int i = 0; i < regCount; i++) {
+            result[i] = ret[i];
+        }
+
+        return result;
+    }
+
+    /**
+     * See Appel 19.6
+     * To remove the phi instructions in an edge-split SSA representation
+     * we know we can always insert a move in a predecessor block
+     */
+    private void removePhiFunctions() {
+        for (SsaBasicBlock block: ssaMeth.getBlocks()) {
+            // Add moves in all the pred blocks for each phi insn`
+            block.forEachPhiInsn(new PhiVisitor(block));
+            // Delete the phi insns
+            block.removeAllPhiInsns();
+        }
+
+        /*
+         * After all move insns have been added: sort them so they don't
+         * destructively interfere
+         */
+        for (SsaBasicBlock block: ssaMeth.getBlocks()) {
+            block.scheduleMovesFromPhis();
+        }
+    }
+
+    /**
+     * PhiSuccessorUpdater for adding move instructions to predecessors based
+     * on phi insns.
+     */
+    private class PhiVisitor implements PhiInsn.Visitor {
+        SsaBasicBlock block;
+
+        PhiVisitor (final SsaBasicBlock block) {
+            this.block = block;
+        }
+
+        public void visitPhiInsn (PhiInsn insn) {
+            RegisterSpecList sources;
+            RegisterSpec result;
+            ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+            sources = insn.getSources();
+            result = insn.getResult();
+
+            int sz = sources.size();
+
+            for (int i = 0; i <sz; i++) {
+                RegisterSpec source;
+
+                source = sources.get(i);
+
+                SsaBasicBlock predBlock;
+
+                predBlock = blocks.get(
+                        insn.predBlockIndexForSourcesIndex(i));
+
+                predBlock.addMoveToEnd(result, source);
+            }
+        }
+    }
+
+    /**
+     * Moves the parameter registers, which allocateRegisters() places
+     * at the bottom of the frame, up to the top of the frame to match
+     * Dalvik calling convention.
+     */
+    private void moveParametersToHighRegisters() {
+
+        int paramWidth = ssaMeth.getParamWidth();
+
+        BasicRegisterMapper mapper
+                = new BasicRegisterMapper(ssaMeth.getRegCount());
+        int regCount = ssaMeth.getRegCount();
+
+        for (int i = 0; i < regCount; i++) {
+            if (i < paramWidth) {
+                mapper.addMapping(i, regCount - paramWidth + i, 1);
+            } else {
+                mapper.addMapping(i, i - paramWidth, 1);
+            }
+        }
+
+        if (DEBUG) {
+            System.out.printf("Moving %d registers from 0 to %d\n",
+                    paramWidth, regCount - paramWidth);
+        }
+
+        ssaMeth.mapRegisters(mapper);
+    }
+
+    private RopMethod convertToRop() {
+        return new RopMethod(convertBasicBlocks(),
+                ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()));
+    }
+
+    /**
+     * @return rop-form basic block list
+     */
+    private BasicBlockList convertBasicBlocks() {
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+        // Exit block may be null
+        SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+
+        int ropBlockCount = ssaMeth.getCountReachableBlocks();
+
+        // Don't count the exit block, if it exists
+        ropBlockCount -= (exitBlock == null) ? 0 : 1;
+
+        BasicBlockList result = new BasicBlockList(ropBlockCount);
+
+        // Convert all the reachable blocks except the exit block
+        int ropBlockIndex = 0;
+        for(SsaBasicBlock b : blocks) {
+            if (b.isReachable() && b != exitBlock) {
+                result.set(ropBlockIndex++, convertBasicBlock(b));
+            }
+        }
+
+        // The exit block, which is discarded, must do nothing.
+        if (exitBlock != null && exitBlock.getInsns().size() != 0) {
+            throw new RuntimeException
+                    ("Exit block must have no insns when leaving SSA form");
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates that a basic block is a valid end predecessor. It must
+     * end in a RETURN or a THROW. Throws a runtime exception on error.
+     *
+     * @param b non-null; block to validate
+     * @throws RuntimeException on error
+     */
+    private void verifyValidExitPredecessor(SsaBasicBlock b) {
+
+        ArrayList<SsaInsn> insns = b.getInsns();
+        SsaInsn lastInsn = insns.get(insns.size() - 1);
+        Rop opcode = lastInsn.getOpcode();
+
+        if (opcode.getBranchingness() != Rop.BRANCH_RETURN
+                && opcode != Rops.THROW) {
+            throw new RuntimeException("Exit predecessor must end"
+                    + " in valid exit statement.");
+        }
+    }
+
+    /**
+     * Converts a single basic block to rop form.
+     *
+     * @param block SSA block to process
+     * @return ROP block
+     */
+    private BasicBlock convertBasicBlock(SsaBasicBlock block) {
+        BasicBlock result;
+        IntList successorList = block.getRopLabelSuccessorList();
+        int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
+        // Filter out any reference to the SSA form's exit block
+
+        // exit block may be null
+        SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+
+        int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
+
+        if (successorList.contains(exitRopLabel)) {
+            if (successorList.size() > 1) {
+                throw new RuntimeException (
+                        "Exit predecessor must have no other successors"
+                                + Hex.u2(block.getRopLabel()));
+            } else {
+                successorList = IntList.EMPTY;
+                primarySuccessorLabel = -1;
+
+                verifyValidExitPredecessor(block);
+            }
+        }
+
+        successorList.setImmutable();
+
+        result = new BasicBlock(
+                block.getRopLabel(), convertInsns(block.getInsns()),
+                successorList,
+                primarySuccessorLabel);
+
+        return result;
+    }
+
+    /**
+     * Converts an insn list to rop form
+     * @param ssaInsns non-null;old instructions
+     * @return non-null; immutable instruction list
+     */
+    private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
+        InsnList result;
+        int insnCount;
+
+        insnCount = ssaInsns.size();
+        result = new InsnList (insnCount);
+
+        for (int i = 0; i < insnCount; i++) {
+            result.set(i, ssaInsns.get(i).toRopInsn());
+        }
+
+        result.setImmutable();
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/package-info.java b/dx/src/com/android/dx/ssa/package-info.java
new file mode 100644
index 0000000..45d9ad6
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/package-info.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+/**
+ * <h1>An introduction to SSA Form</h1>
+ *
+ * This package contains classes associated with dx's <code>SSA</code>
+ * intermediate form. This form is a static-single-assignment representation of
+ * Rop-form a method with Rop-form-like instructions (with the addition of a
+ * {@link PhiInsn phi instriction}. This form is intended to make it easy to
+ * implement basic optimization steps and register allocation so that a
+ * reasonably efficient register machine representation can be produced from a
+ * stack machine source bytecode.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <h3>Classes related to conversion and lifetime</h3>
+ * <ul>
+ * <li> {@link Optimizer} is a singleton class containing methods for
+ * converting, optimizing, and then back-converting Rop-form methods. It's the
+ * typical gateway into the rest of the package.
+ * <li> {@link SsaConverter} converts a Rop-form method to SSA form.
+ * <li> {@link SsaToRop} converts an SSA-form method back to Rop form.
+ * </ul>
+ *
+ * <h3>Classes related to method representation</h3>
+ * <ul>
+ * <li> A {@link SsaMethod} instance represents a method.
+ * <li> A {@link SsaBasicBlock} instance represents a basic block, whose
+ * semantics are quite similar to basic blocks in
+ * {@link com.android.dx.rop Rop form}.
+ * <li> {@link PhiInsn} instances represent "phi" operators defined in SSA
+ * literature. They must be the first N instructions in a basic block.
+ * <li> {@link NormalSsaInsn} instances represent instructions that directly
+ * correspond to <code>Rop</code> form.
+ * </ul>
+ *
+ * <h3>Classes related to optimization steps</h3>
+ * <ul>
+ * <li> {@link MoveParamCombiner} is a simple step that ensures each method
+ * parameter is represented by at most one SSA register.
+ * <li> {@link SCCP} is a (partially implemented) sparse-conditional
+ * constant propogator.
+ * <li> {@link LiteralOpUpgrader} is a step that attempts to use constant
+ * information to convert math and comparison instructions into
+ * constant-bearing "literal ops" in cases where they can be represented in the
+ * output form (see {@link TranslationAdvice#hasConstantOperation}).
+ * <li> {@link ConstCollector} is a step that attempts to trade (modest)
+ * increased register space for decreased instruction count in cases where
+ * the same constant value is used repeatedly in a single method.
+ * <li> {@link DeadCodeRemover} is a dead code remover. This phase must
+ * always be run to remove unused phi instructions.
+ * </ul>
+ *
+ * <h2>SSA Lifetime</h2>
+ * The representation of a method in SSA form obeys slightly different
+ * constraints depending upon whether it is in the process of being converted
+ * into or out of SSA form.
+ *
+ * <h3>Conversion into SSA Form</h3>
+ *
+ * {@link SsaConverter#convertToSsaMethod} takes a <code>RopMethod</code> and
+ * returns a fully-converted <code>SsaMethod</code>. The conversion process
+ * is roughly as follows:
+ *
+ * <ol>
+ * <li> The Rop-form method, its blocks and their instructions are directly
+ * wrapped in <code>SsaMethod</code>, <code>SsaBasicBlock</code> and
+ * <code>SsaInsn</code> instances. Nothing else changes.
+ * <li> Critical control-flow graph edges are {@link SsaConverter#edgeSplit
+ * split} and new basic blocks inserted as required to meet the constraints
+ * necessary for the ultimate SSA representation.
+ * <li> A {@link LocalVariableExtractor} is run to produce a table of
+ * Rop registers to local variables necessary during phi placement. This
+ * step could also be done in Rop form and then updated through the preceding
+ * steps.
+ * <li> <code>Phi</code> instructions are {link SsaConverter#placePhiFunctions}
+ * placed in a semi-pruned fashion, which requires computation of {@link
+ * Dominators dominance graph} and each node's {@link DomFront
+ * dominance-frontier set}.
+ * <li> Finally, source and result registers for all instructions are {@link
+ * SsaRenamer renamed} such that each assignment is given a unique register
+ * number (register categories or widths, significant in Rop form, do not
+ * exist in SSA). Move instructions are eliminated except where necessary
+ * to preserve local variable assignments.
+ * </ol>
+ *
+ */
diff --git a/dx/src/com/android/dx/util/AnnotatedOutput.java b/dx/src/com/android/dx/util/AnnotatedOutput.java
new file mode 100644
index 0000000..0d95041
--- /dev/null
+++ b/dx/src/com/android/dx/util/AnnotatedOutput.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a binary output destination that may be augmented
+ * with textual annotations.
+ */
+public interface AnnotatedOutput
+        extends Output {
+    /**
+     * Get whether this instance will actually keep annotations.
+     * 
+     * @return <code>true</code> iff annotations are being kept
+     */
+    public boolean annotates();
+
+    /**
+     * Get whether this instance is intended to keep verbose annotations.
+     * Annotators may use the result of calling this method to inform their
+     * annotation activity.
+     * 
+     * @return <code>true</code> iff annotations are to be verbose
+     */
+    public boolean isVerbose();
+
+    /**
+     * Add an annotation for the subsequent output. Any previously
+     * open annotation will be closed by this call, and the new
+     * annotation marks all subsequent output until another annotation
+     * call.
+     * 
+     * @param msg non-null; the annotation message
+     */
+    public void annotate(String msg);
+
+    /**
+     * Add an annotation for a specified amount of subsequent
+     * output. Any previously open annotation will be closed by this
+     * call. If there is already pending annotation from one or more
+     * previous calls to this method, the new call "consumes" output
+     * after all the output covered by the previous calls.
+     * 
+     * @param amt &gt;= 0; the amount of output for this annotation to
+     * cover
+     * @param msg non-null; the annotation message
+     */
+    public void annotate(int amt, String msg);
+
+    /**
+     * End the most recent annotation. Subsequent output will be unannotated,
+     * until the next call to {@link #annotate}.
+     */
+    public void endAnnotation();
+
+    /**
+     * Get the maximum width of the annotated output. This is advisory:
+     * Implementations of this interface are encouraged to deal with too-wide
+     * output, but annotaters are encouraged to attempt to avoid exceeding
+     * the indicated width.
+     * 
+     * @return &gt;= 1; the maximum width
+     */
+    public int getAnnotationWidth();
+}
diff --git a/dx/src/com/android/dx/util/BitIntSet.java b/dx/src/com/android/dx/util/BitIntSet.java
new file mode 100644
index 0000000..c8588f8
--- /dev/null
+++ b/dx/src/com/android/dx/util/BitIntSet.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a bit set
+ */
+public class BitIntSet implements IntSet {
+
+    /** also accessed in ListIntSet */
+    int[] bits;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param max the maximum value of ints in this set.
+     */
+    public BitIntSet(int max) {
+        bits = Bits.makeBitSet(max);
+    }
+
+    /** @inheritDoc */
+    public void add(int value) {
+        ensureCapacity(value);
+        Bits.set(bits, value, true);
+    }
+
+    /**
+     * Ensures that the bit set has the capacity to represent the given value.
+     *
+     * @param value &gt;= 0 value to represent
+     */
+    private void ensureCapacity(int value) {
+        if (value >= Bits.getMax(bits)) {
+            int[] newBits = Bits.makeBitSet(
+                    Math.max(value + 1, 2 * Bits.getMax(bits)));
+            System.arraycopy(bits, 0, newBits, 0, bits.length);
+            bits = newBits;
+        }
+    }
+
+    /** @inheritDoc */
+    public void remove(int value) {
+        if (value < Bits.getMax(bits)) {
+            Bits.set(bits, value, false);
+        }
+    }
+
+    /** @inheritDoc */
+    public boolean has(int value) {
+        return (value < Bits.getMax(bits)) && Bits.get(bits, value);    
+    }
+
+    /** @inheritDoc */
+    public void merge(IntSet other) {
+        if (other instanceof BitIntSet) {
+            BitIntSet o = (BitIntSet) other;
+            ensureCapacity(Bits.getMax(o.bits) + 1);
+            Bits.or(bits, o.bits);
+        } else if (other instanceof ListIntSet) {
+            ListIntSet o = (ListIntSet) other;
+            int sz = o.ints.size();
+
+            if (sz > 0) {
+                ensureCapacity(o.ints.get(sz - 1));
+            }
+            for (int i = 0; i < o.ints.size(); i++) {
+                Bits.set(bits, o.ints.get(i), true);
+            }
+        } else {
+            IntIterator iter = other.iterator();
+            while (iter.hasNext()) {
+                add(iter.next());
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int elements() {
+        return Bits.bitCount(bits);        
+    }
+
+    /** @inheritDoc */
+    public IntIterator iterator() {
+        return new IntIterator() {
+            private int idx = Bits.findFirst(bits, 0);
+
+            /** @inheritDoc */
+            public boolean hasNext() {
+                return idx >= 0;
+            }
+
+            /** @inheritDoc */
+            public int next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                int ret = idx;
+
+                idx = Bits.findFirst(bits, idx+1);
+
+                return ret;
+            }
+
+            /** @inheritDoc */
+            public void remove() {
+                BitIntSet.this.remove(idx);
+                idx = Bits.findFirst(bits, idx+1);
+            }
+        };
+    }
+
+    /** @inheritDoc */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append('{');
+
+        boolean first = true;
+        for (int i = Bits.findFirst(bits, 0)
+                ; i >= 0
+                ; i = Bits.findFirst(bits, i + 1)) {
+            if (!first) {
+                sb.append(", ");
+            }
+            first = false;
+            sb.append(i);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/Bits.java b/dx/src/com/android/dx/util/Bits.java
new file mode 100644
index 0000000..0bc124c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Bits.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for treating <code>int[]</code>s as bit sets.
+ */
+public final class Bits {
+    /**
+     * This class is uninstantiable.
+     */
+    private Bits() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Constructs a bit set to contain bits up to the given index (exclusive).
+     * 
+     * @param max &gt;= 0; the maximum bit index (exclusive)
+     * @return non-null; an appropriately-constructed instance
+     */
+    public static int[] makeBitSet(int max) {
+        int size = (max + 0x1f) >> 5;
+        return new int[size];
+    }
+
+    /**
+     * Gets the maximum index (exclusive) for the given bit set.
+     * 
+     * @param bits non-null; bit set in question
+     * @return &gt;= 0; the maximum index (exclusive) that may be set
+     */
+    public static int getMax(int[] bits) {
+        return bits.length * 0x20;
+    }
+
+    /**
+     * Gets the value of the bit at the given index.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param idx &gt;= 0, &lt; getMax(set); which bit
+     * @return the value of the indicated bit
+     */
+    public static boolean get(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        return (bits[arrayIdx] & bit) != 0;
+    }
+
+    /**
+     * Sets the given bit to the given value.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param idx &gt;= 0, &lt; getMax(set); which bit
+     * @param value the new value for the bit
+     */
+    public static void set(int[] bits, int idx, boolean value) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+
+        if (value) {
+            bits[arrayIdx] |= bit;
+        } else {
+            bits[arrayIdx] &= ~bit;
+        }
+    }
+
+    /**
+     * Sets the given bit to <code>true</code>.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param idx &gt;= 0, &lt; getMax(set); which bit
+     */
+    public static void set(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        bits[arrayIdx] |= bit;
+    }
+
+    /**
+     * Sets the given bit to <code>false</code>.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param idx &gt;= 0, &lt; getMax(set); which bit
+     */
+    public static void clear(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        bits[arrayIdx] &= ~bit;
+    }
+
+    /**
+     * Returns whether or not the given bit set is empty, that is, whether
+     * no bit is set to <code>true</code>.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @return <code>true</code> iff all bits are <code>false</code>
+     */
+    public static boolean isEmpty(int[] bits) {
+        int len = bits.length;
+
+        for (int i = 0; i < len; i++) {
+            if (bits[i] != 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Gets the number of bits set to <code>true</code> in the given bit set.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @return &gt;= 0; the bit count (aka population count) of the set
+     */
+    public static int bitCount(int[] bits) {
+        int len = bits.length;
+        int count = 0;
+
+        for (int i = 0; i < len; i++) {
+            count += Integer.bitCount(bits[i]);
+        }
+
+        return count;
+    }
+
+    /**
+     * Returns whether any bits are set to <code>true</code> in the
+     * specified range.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param start &gt;= 0; index of the first bit in the range (inclusive)
+     * @param end &gt;= 0; index of the last bit in the range (exclusive)
+     * @return <code>true</code> if any bit is set to <code>true</code> in
+     * the indicated range
+     */
+    public static boolean anyInRange(int[] bits, int start, int end) {
+        int idx = findFirst(bits, start);
+        return (idx >= 0) && (idx < end);
+    }
+
+    /**
+     * Finds the lowest-order bit set at or after the given index in the
+     * given bit set.
+     * 
+     * @param bits non-null; bit set to operate on
+     * @param idx &gt;= 0; minimum index to return
+     * @return &gt;= -1; lowest-order bit set at or after <code>idx</code>,
+     * or <code>-1</code> if there is no appropriate bit index to return
+     */
+    public static int findFirst(int[] bits, int idx) {
+        int len = bits.length;
+        int minBit = idx & 0x1f;
+
+        for (int arrayIdx = idx >> 5; arrayIdx < len; arrayIdx++) {
+            int word = bits[arrayIdx];
+            if (word != 0) {
+                int bitIdx = findFirst(word, minBit);
+                if (bitIdx >= 0) {
+                    return (arrayIdx << 5) + bitIdx;
+                }
+            }
+            minBit = 0;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Finds the lowest-order bit set at or after the given index in the
+     * given <code>int</code>.
+     * 
+     * @param value the value in question
+     * @param idx 0..31 the minimum bit index to return
+     * @return &gt;= -1; lowest-order bit set at or after <code>idx</code>,
+     * or <code>-1</code> if there is no appropriate bit index to return
+     */
+    public static int findFirst(int value, int idx) {
+        value &= ~((1 << idx) - 1); // Mask off too-low bits.
+        int result = Integer.numberOfTrailingZeros(value);
+        return (result == 32) ? -1 : result;
+    }
+
+    /**
+     * Ors bit array <code>b</code> into bit array <code>a</code>.
+     * <code>a.length</code> must be greater than or equal to
+     * <code>b.length</code>.
+     *
+     * @param a non-null; int array to be ored with other argument. This
+     * argument is modified.
+     * @param b non-null; int array to be ored into <code>a</code>. This
+     * argument is not modified.
+     */
+    public static void or(int[] a, int[] b) {
+        for (int i = 0; i < b.length; i++) {
+            a[i] |= b[i];
+        }
+    }
+
+    public static String toHuman(int[] bits) {
+        StringBuilder sb = new StringBuilder();
+
+        boolean needsComma = false;
+
+        sb.append('{');
+
+        int bitsLength = 32 * bits.length;
+        for (int i = 0; i < bitsLength; i++) {
+            if (Bits.get(bits, i)) {
+                if (needsComma) {
+                    sb.append(',');
+                }
+                needsComma = true;
+                sb.append(i);
+            }
+        }
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/ByteArray.java b/dx/src/com/android/dx/util/ByteArray.java
new file mode 100644
index 0000000..3fcf293
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArray.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Wrapper for a <code>byte[]</code>, which provides read-only access and
+ * can "reveal" a partial slice of the underlying array.
+ *
+ * <b>Note:</b> Multibyte accessors all use big-endian order.
+ */
+public final class ByteArray {
+    /** non-null; underlying array */
+    private final byte[] bytes;
+
+    /** <code>&gt;= 0</code>; start index of the slice (inclusive) */
+    private final int start;
+
+    /** <code>&gt;= 0, &lt;= bytes.length</code>; size computed as
+     * <code>end - start</code> (in the constructor) */
+    private final int size;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes non-null; the underlying array
+     * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
+     * @param end <code>&gt;= start, &lt;= bytes.length</code>; end index of
+     * the slice (exclusive)
+     */
+    public ByteArray(byte[] bytes, int start, int end) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (start < 0) {
+            throw new IllegalArgumentException("start < 0");
+        }
+
+        if (end < start) {
+            throw new IllegalArgumentException("end < start");
+        }
+
+        if (end > bytes.length) {
+            throw new IllegalArgumentException("end > bytes.length");
+        }
+
+        this.bytes = bytes;
+        this.start = start;
+        this.size = end - start;
+    }
+
+    /**
+     * Constructs an instance from an entire <code>byte[]</code>.
+     *
+     * @param bytes non-null; the underlying array
+     */
+    public ByteArray(byte[] bytes) {
+        this(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Gets the size of the array, in bytes.
+     *
+     * @return &gt;= 0; the size
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns a slice (that is, a sub-array) of this instance.
+     *
+     * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
+     * @param end <code>&gt;= start, &lt;= size()</code>; end index of
+     * the slice (exclusive)
+     * @return non-null; the slice
+     */
+    public ByteArray slice(int start, int end) {
+        checkOffsets(start, end);
+        return new ByteArray(bytes, start + this.start, end + this.start);
+    }
+
+    /**
+     * Returns the offset into the given array represented by the given
+     * offset into this instance.
+     *
+     * @param offset offset into this instance
+     * @param bytes non-null; (alleged) underlying array
+     * @return corresponding offset into <code>bytes</code>
+     * @throws IllegalArgumentException thrown if <code>bytes</code> is
+     * not the underlying array of this instance
+     */
+    public int underlyingOffset(int offset, byte[] bytes) {
+        if (bytes != this.bytes) {
+            throw new IllegalArgumentException("wrong bytes");
+        }
+
+        return start + offset;
+    }
+
+    /**
+     * Gets the <code>signed byte</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; size(); offset to fetch
+     * @return <code>signed byte</code> at that offset
+     */
+    public int getByte(int off) {
+        checkOffsets(off, off + 1);
+        return getByte0(off);
+    }
+
+    /**
+     * Gets the <code>signed short</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
+     * @return <code>signed short</code> at that offset
+     */
+    public int getShort(int off) {
+        checkOffsets(off, off + 2);
+        return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
+    }
+
+    /**
+     * Gets the <code>signed int</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; (size() - 3); offset to fetch
+     * @return <code>signed int</code> at that offset
+     */
+    public int getInt(int off) {
+        checkOffsets(off, off + 4);
+        return (getByte0(off) << 24) |
+            (getUnsignedByte0(off + 1) << 16) |
+            (getUnsignedByte0(off + 2) << 8) |
+            getUnsignedByte0(off + 3);
+    }
+
+    /**
+     * Gets the <code>signed long</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; (size() - 7); offset to fetch
+     * @return <code>signed int</code> at that offset
+     */
+    public long getLong(int off) {
+        checkOffsets(off, off + 8);
+        int part1 = (getByte0(off) << 24) |
+            (getUnsignedByte0(off + 1) << 16) |
+            (getUnsignedByte0(off + 2) << 8) |
+            getUnsignedByte0(off + 3);
+        int part2 = (getByte0(off + 4) << 24) |
+            (getUnsignedByte0(off + 5) << 16) |
+            (getUnsignedByte0(off + 6) << 8) |
+            getUnsignedByte0(off + 7);
+
+        return (part2 & 0xffffffffL) | ((long) part1) << 32;
+    }
+
+    /**
+     * Gets the <code>unsigned byte</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; size(); offset to fetch
+     * @return <code>unsigned byte</code> at that offset
+     */
+    public int getUnsignedByte(int off) {
+        checkOffsets(off, off + 1);
+        return getUnsignedByte0(off);
+    }
+
+    /**
+     * Gets the <code>unsigned short</code> value at a particular offset.
+     *
+     * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
+     * @return <code>unsigned short</code> at that offset
+     */
+    public int getUnsignedShort(int off) {
+        checkOffsets(off, off + 2);
+        return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
+    }
+
+    /**
+     * Copies the contents of this instance into the given raw
+     * <code>byte[]</code> at the given offset. The given array must be
+     * large enough.
+     * 
+     * @param out non-null; array to hold the output
+     * @param offset non-null; index into <code>out</code> for the first
+     * byte of output
+     */
+    public void getBytes(byte[] out, int offset) {
+        if ((out.length - offset) < size) {
+            throw new IndexOutOfBoundsException("(out.length - offset) < " +
+                                                "size()");
+        }
+
+        System.arraycopy(bytes, start, out, offset, size);
+    }
+
+    /**
+     * Checks a range of offsets for validity, throwing if invalid.
+     *
+     * @param s start offset (inclusive)
+     * @param e end offset (exclusive)
+     */
+    private void checkOffsets(int s, int e) {
+        if ((s < 0) || (e < s) || (e > size)) {
+            throw new IllegalArgumentException("bad range: " + s + ".." + e +
+                                               "; actual size " + size);
+        }
+    }
+
+    /**
+     * Gets the <code>signed byte</code> value at the given offset,
+     * without doing any argument checking.
+     *
+     * @param off offset to fetch
+     * @return byte at that offset
+     */
+    private int getByte0(int off) {
+        return bytes[start + off];
+    }
+
+    /**
+     * Gets the <code>unsigned byte</code> value at the given offset,
+     * without doing any argument checking.
+     *
+     * @param off offset to fetch
+     * @return byte at that offset
+     */
+    private int getUnsignedByte0(int off) {
+        return bytes[start + off] & 0xff;
+    }
+
+    /**
+     * Gets a <code>DataInputStream</code> that reads from this instance,
+     * with the cursor starting at the beginning of this instance's data.
+     * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+     * if needed.
+     * 
+     * @return non-null; an appropriately-constructed
+     * <code>DataInputStream</code> instance
+     */
+    public MyDataInputStream makeDataInputStream() {
+        return new MyDataInputStream(makeInputStream());
+    }
+
+    /**
+     * Gets a <code>InputStream</code> that reads from this instance,
+     * with the cursor starting at the beginning of this instance's data.
+     * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+     * if needed.
+     * 
+     * @return non-null; an appropriately-constructed
+     * <code>InputStream</code> instancex
+     */
+    public MyInputStream makeInputStream() {
+        return new MyInputStream();
+    }
+
+    /**
+     * Helper interface that allows one to get the cursor (of a stream).
+     */
+    public interface GetCursor {
+        /**
+         * Gets the current cursor.
+         * 
+         * @return 0..size(); the cursor
+         */
+        public int getCursor();
+    }
+     
+    /**
+     * Helper class for {@link #makeInputStream}, which implements the
+     * stream functionality.
+     */
+    public class MyInputStream extends InputStream {
+        /** 0..size; the cursor */
+        private int cursor;
+
+        /** 0..size; the mark */
+        private int mark;
+        
+        public MyInputStream() {
+            cursor = 0;
+            mark = 0;
+        }
+
+        public int read() throws IOException {
+            if (cursor >= size) {
+                return -1;
+            }
+
+            int result = getUnsignedByte0(cursor);
+            cursor++;
+            return result;
+        }
+
+        public int read(byte[] arr, int offset, int length) {
+            if ((offset + length) > arr.length) {
+                length = arr.length - offset;
+            }
+            
+            int maxLength = size - cursor;
+            if (length > maxLength) {
+                length = maxLength;
+            }
+
+            System.arraycopy(bytes, cursor, arr, offset, length);
+            cursor += length;
+            return length;
+        }
+
+        public int available() {
+            return size - cursor;
+        }
+
+        public void mark(int reserve) {
+            mark = cursor;
+        }
+
+        public void reset() {
+            cursor = mark;
+        }
+
+        public boolean markSupported() {
+            return true;
+        }
+
+        /**
+         * Gets the current cursor.
+         * 
+         * @return 0..size(); the cursor
+         */
+        public int getCursor() {
+            return cursor;
+        }
+    }
+
+    /**
+     * Helper class for {@link #makeDataInputStream}. This is used
+     * simply so that the cursor of a wrapped {@link #MyInputStream}
+     * instance may be easily determined.
+     */
+    public class MyDataInputStream extends DataInputStream {
+        /** non-null; the underlying {@link #MyInputStream} */
+        private final MyInputStream wrapped;
+        
+        public MyDataInputStream(MyInputStream wrapped) {
+            super(wrapped);
+
+            this.wrapped = wrapped;
+        }
+
+        /**
+         * Gets the current cursor.
+         * 
+         * @return 0..size(); the cursor
+         */
+        public int getCursor() {
+            return wrapped.getCursor();
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 0000000..457a603
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a <code>byte[]</code>.
+ * 
+ * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.</p>
+ */
+public final class ByteArrayAnnotatedOutput
+        implements AnnotatedOutput {
+    /** default size for stretchy instances */
+    private static final int DEFAULT_SIZE = 1000;
+    
+    /**
+     * whether the instance is stretchy, that is, whether its array
+     * may be resized to increase capacity
+     */
+    private final boolean stretchy;
+
+    /** non-null; the data itself */
+    private byte[] data;
+
+    /** &gt;= 0; current output cursor */
+    private int cursor;
+
+    /** whether annotations are to be verbose */
+    private boolean verbose;
+
+    /**
+     * null-ok; list of annotations, or <code>null</code> if this instance
+     * isn't keeping them 
+     */
+    private ArrayList<Annotation> annotations;
+
+    /** &gt;= 40 (if used); the desired maximum annotation width */
+    private int annotationWidth;
+
+    /**
+     * &gt;= 8 (if used); the number of bytes of hex output to use
+     * in annotations 
+     */
+    private int hexCols;
+
+    /**
+     * Constructs an instance with a fixed maximum size. Note that the
+     * given array is the only one that will be used to store data. In
+     * particular, no reallocation will occur in order to expand the
+     * capacity of the resulting instance. Also, the constructed
+     * instance does not keep annotations by default.
+     * 
+     * @param data non-null; data array to use for output
+     */
+    public ByteArrayAnnotatedOutput(byte[] data) {
+        this(data, false);
+    }
+
+    /**
+     * Constructs a "stretchy" instance. The underlying array may be
+     * reallocated. The constructed instance does not keep annotations
+     * by default.
+     */
+    public ByteArrayAnnotatedOutput() {
+        this(new byte[DEFAULT_SIZE], true);
+    }
+
+    /**
+     * Internal constructor.
+     * 
+     * @param data non-null; data array to use for output
+     * @param stretchy whether the instance is to be stretchy
+     */
+    private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
+        if (data == null) {
+            throw new NullPointerException("data == null");
+        }
+
+        this.stretchy = stretchy;
+        this.data = data;
+        this.cursor = 0;
+        this.verbose = false;
+        this.annotations = null;
+        this.annotationWidth = 0;
+        this.hexCols = 0;
+    }
+
+    /**
+     * Gets the underlying <code>byte[]</code> of this instance, which
+     * may be larger than the number of bytes written
+     * 
+     * @see #toByteArray
+     * 
+     * @return non-null; the <code>byte[]</code>
+     */
+    public byte[] getArray() {
+        return data;
+    }
+
+    /**
+     * Constructs and returns a new <code>byte[]</code> that contains
+     * the written contents exactly (that is, with no extra unwritten
+     * bytes at the end).
+     * 
+     * @see #getArray
+     * 
+     * @return non-null; an appropriately-constructed array
+     */
+    public byte[] toByteArray() {
+        byte[] result = new byte[cursor];
+        System.arraycopy(data, 0, result, 0, cursor);
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public int getCursor() {
+        return cursor;
+    }
+
+    /** {@inheritDoc} */
+    public void assertCursor(int expectedCursor) {
+        if (cursor != expectedCursor) {
+            throw new ExceptionWithContext("expected cursor " +
+                    expectedCursor + "; actual value: " + cursor);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void writeByte(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 1;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeShort(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 2;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        data[writeAt + 1] = (byte) (value >> 8);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeInt(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 4;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        data[writeAt + 1] = (byte) (value >> 8);
+        data[writeAt + 2] = (byte) (value >> 16);
+        data[writeAt + 3] = (byte) (value >> 24);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeLong(long value) {
+        int writeAt = cursor;
+        int end = writeAt + 8;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        int half = (int) value;
+        data[writeAt] = (byte) half;
+        data[writeAt + 1] = (byte) (half >> 8);
+        data[writeAt + 2] = (byte) (half >> 16);
+        data[writeAt + 3] = (byte) (half >> 24);
+
+        half = (int) (value >> 32);
+        data[writeAt + 4] = (byte) half;
+        data[writeAt + 5] = (byte) (half >> 8);
+        data[writeAt + 6] = (byte) (half >> 16);
+        data[writeAt + 7] = (byte) (half >> 24);
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public int writeUnsignedLeb128(int value) {
+        int remaining = value >> 7;
+        int count = 0;
+
+        while (remaining != 0) {
+            writeByte((value & 0x7f) | 0x80);
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        writeByte(value & 0x7f);
+        return count + 1;
+    }
+
+    /** {@inheritDoc} */
+    public int writeSignedLeb128(int value) {
+        int remaining = value >> 7;
+        int count = 0;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                || ((remaining & 1) != ((value >> 6) & 1));
+
+            writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        return count;
+    }
+    
+    /** {@inheritDoc} */
+    public void write(ByteArray bytes) {
+        int blen = bytes.size();
+        int writeAt = cursor;
+        int end = writeAt + blen;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        bytes.getBytes(data, writeAt);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void write(byte[] bytes, int offset, int length) {
+        int writeAt = cursor;
+        int end = writeAt + length;
+        int bytesEnd = offset + length;
+
+        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+        if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+            throw new IndexOutOfBoundsException("bytes.length " +
+                                                bytes.length + "; " + 
+                                                offset + "..!" + end);
+        }
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        System.arraycopy(bytes, offset, data, writeAt, length);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void write(byte[] bytes) {
+        write(bytes, 0, bytes.length);
+    }
+
+    /** {@inheritDoc} */
+    public void writeZeroes(int count) {
+        if (count < 0) {
+            throw new IllegalArgumentException("count < 0");
+        }
+
+        int end = cursor + count;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        /*
+         * There is no need to actually write zeroes, since the array is
+         * already preinitialized with zeroes.
+         */
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void alignTo(int alignment) {
+        int mask = alignment - 1;
+
+        if ((alignment < 0) || ((mask & alignment) != 0)) {
+            throw new IllegalArgumentException("bogus alignment");
+        }
+
+        int end = (cursor + mask) & ~mask;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        /*
+         * There is no need to actually write zeroes, since the array is
+         * already preinitialized with zeroes.
+         */
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public boolean annotates() {
+        return (annotations != null);
+    }
+
+    /** {@inheritDoc} */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    /** {@inheritDoc} */
+    public void annotate(String msg) {
+        if (annotations == null) {
+            return;
+        }
+
+        endAnnotation();
+        annotations.add(new Annotation(cursor, msg));
+    }
+
+    /** {@inheritDoc} */
+    public void annotate(int amt, String msg) {
+        if (annotations == null) {
+            return;
+        }
+
+        endAnnotation();
+
+        int asz = annotations.size();
+        int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+        int startAt;
+
+        if (lastEnd <= cursor) {
+            startAt = cursor;
+        } else {
+            startAt = lastEnd;
+        }
+
+        annotations.add(new Annotation(startAt, startAt + amt, msg));
+    }
+
+    /** {@inheritDoc} */
+    public void endAnnotation() {
+        if (annotations == null) {
+            return;
+        }
+
+        int sz = annotations.size();
+
+        if (sz != 0) {
+            annotations.get(sz - 1).setEndIfUnset(cursor);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int getAnnotationWidth() {
+        int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+        return annotationWidth - leftWidth;
+    }
+
+    /**
+     * Indicates that this instance should keep annotations. This method may
+     * be called only once per instance, and only before any data has been
+     * written to the it.
+     * 
+     * @param annotationWidth &gt;= 40; the desired maximum annotation width
+     * @param verbose whether or not to indicate verbose annotations
+     */
+    public void enableAnnotations(int annotationWidth, boolean verbose) {
+        if ((annotations != null) || (cursor != 0)) {
+            throw new RuntimeException("cannot enable annotations");
+        }
+
+        if (annotationWidth < 40) {
+            throw new IllegalArgumentException("annotationWidth < 40");
+        }
+
+        int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+        if (hexCols < 6) {
+            hexCols = 6;
+        } else if (hexCols > 10) {
+            hexCols = 10;
+        }
+
+        this.annotations = new ArrayList<Annotation>(1000);
+        this.annotationWidth = annotationWidth;
+        this.hexCols = hexCols;
+        this.verbose = verbose;
+    }
+
+    /**
+     * Finishes up annotation processing. This closes off any open
+     * annotations and removes annotations that don't refer to written
+     * data.
+     */
+    public void finishAnnotating() {
+        // Close off the final annotation, if any.
+        endAnnotation();
+
+        // Remove annotations that refer to unwritten data.
+        if (annotations != null) {
+            int asz = annotations.size();
+            while (asz > 0) {
+                Annotation last = annotations.get(asz - 1);
+                if (last.getStart() > cursor) {
+                    annotations.remove(asz - 1);
+                    asz--;
+                } else if (last.getEnd() > cursor) {
+                    last.setEnd(cursor);
+                    break;
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Writes the annotated content of this instance to the given writer.
+     * 
+     * @param out non-null; where to write to
+     */
+    public void writeAnnotationsTo(Writer out) throws IOException {
+        int width2 = getAnnotationWidth();
+        int width1 = annotationWidth - width2 - 1;
+
+        TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
+        Writer left = twoc.getLeft();
+        Writer right = twoc.getRight();
+        int leftAt = 0; // left-hand byte output cursor
+        int rightAt = 0; // right-hand annotation index
+        int rightSz = annotations.size();
+
+        while ((leftAt < cursor) && (rightAt < rightSz)) {
+            Annotation a = annotations.get(rightAt);
+            int start = a.getStart();
+            int end;
+            String text;
+
+            if (leftAt < start) {
+                // This is an area with no annotation.
+                end = start;
+                start = leftAt;
+                text = "";
+            } else {
+                // This is an area with an annotation.
+                end = a.getEnd();
+                text = a.getText();
+                rightAt++;
+            }
+
+            left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
+            right.write(text);
+            twoc.flush();
+            leftAt = end;
+        }
+
+        if (leftAt < cursor) {
+            // There is unannotated output at the end.
+            left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
+                                hexCols, 6));
+        }
+
+        while (rightAt < rightSz) {
+            // There are zero-byte annotations at the end.
+            right.write(annotations.get(rightAt).getText());
+            rightAt++;
+        }
+
+        twoc.flush();
+    }
+
+    /**
+     * Throws the excpetion for when an attempt is made to write past the
+     * end of the instance.
+     */
+    private static void throwBounds() {
+        throw new IndexOutOfBoundsException("attempt to write past the end");
+    }
+
+    /**
+     * Reallocates the underlying array if necessary. Calls to this method
+     * should be guarded by a test of {@link #stretchy}.
+     * 
+     * @param desiredSize &gt;= 0; the desired minimum total size of the array
+     */
+    private void ensureCapacity(int desiredSize) {
+        if (data.length < desiredSize) {
+            byte[] newData = new byte[desiredSize * 2 + 1000];
+            System.arraycopy(data, 0, newData, 0, cursor);
+            data = newData;
+        }
+    }
+
+    /**
+     * Annotation on output.
+     */
+    private static class Annotation {
+        /** &gt;= 0; start of annotated range (inclusive) */
+        private final int start;
+
+        /**
+         * &gt;= 0; end of annotated range (exclusive);
+         * <code>Integer.MAX_VALUE</code> if unclosed 
+         */
+        private int end;
+
+        /** non-null; annotation text */
+        private final String text;
+
+        /**
+         * Constructs an instance.
+         * 
+         * @param start &gt;= 0; start of annotated range
+         * @param end &gt;= start; end of annotated range (exclusive) or
+         * <code>Integer.MAX_VALUE</code> if unclosed
+         * @param text non-null; annotation text
+         */
+        public Annotation(int start, int end, String text) {
+            this.start = start;
+            this.end = end;
+            this.text = text;
+        }
+
+        /**
+         * Constructs an instance. It is initally unclosed.
+         * 
+         * @param start &gt;= 0; start of annotated range
+         * @param text non-null; annotation text
+         */
+        public Annotation(int start, String text) {
+            this(start, Integer.MAX_VALUE, text);
+        }
+
+        /**
+         * Sets the end as given, but only if the instance is unclosed;
+         * otherwise, do nothing.
+         * 
+         * @param end &gt;= start; the end
+         */
+        public void setEndIfUnset(int end) {
+            if (this.end == Integer.MAX_VALUE) {
+                this.end = end;
+            }
+        }
+
+        /**
+         * Sets the end as given.
+         * 
+         * @param end &gt;= start; the end
+         */
+        public void setEnd(int end) {
+            this.end = end;
+        }
+
+        /**
+         * Gets the start.
+         * 
+         * @return the start
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Gets the end.
+         * 
+         * @return the end
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the text.
+         * 
+         * @return non-null; the text
+         */
+        public String getText() {
+            return text;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/ExceptionWithContext.java b/dx/src/com/android/dx/util/ExceptionWithContext.java
new file mode 100644
index 0000000..035546e
--- /dev/null
+++ b/dx/src/com/android/dx/util/ExceptionWithContext.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext
+        extends RuntimeException {
+    /** non-null; human-oriented context of the exception */
+    private StringBuffer context;
+
+    /**
+     * Augments the given exception with the given context, and return the
+     * result. The result is either the given exception if it was an
+     * {@link ExceptionWithContext}, or a newly-constructed exception if it
+     * was not.
+     *
+     * @param ex non-null; the exception to augment
+     * @param str non-null; context to add
+     * @return non-null; an appropriate instance
+     */
+    public static ExceptionWithContext withContext(Throwable ex, String str) {
+        ExceptionWithContext ewc;
+
+        if (ex instanceof ExceptionWithContext) {
+            ewc = (ExceptionWithContext) ex;
+        } else {
+            ewc = new ExceptionWithContext(ex);
+        }
+
+        ewc.addContext(str);
+        return ewc;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     */
+    public ExceptionWithContext(String message) {
+        this(message, null);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cause null-ok; exception that caused this one
+     */
+    public ExceptionWithContext(Throwable cause) {
+        this(null, cause);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     * @param cause null-ok; exception that caused this one
+     */
+    public ExceptionWithContext(String message, Throwable cause) {
+        super((message != null) ? message :
+              (cause != null) ? cause.getMessage() : null,
+              cause);
+
+        if (cause instanceof ExceptionWithContext) {
+            String ctx = ((ExceptionWithContext) cause).context.toString();
+            context = new StringBuffer(ctx.length() + 200);
+            context.append(ctx);
+        } else {
+            context = new StringBuffer(200);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintStream out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintWriter out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /**
+     * Adds a line of context to this instance.
+     *
+     * @param str non-null; new context
+     */
+    public void addContext(String str) {
+        if (str == null) {
+            throw new NullPointerException("str == null");
+        }
+
+        context.append(str);
+        if (!str.endsWith("\n")) {
+            context.append('\n');
+        }
+    }
+
+    /**
+     * Gets the context.
+     *
+     * @return non-null; the context
+     */
+    public String getContext() {
+        return context.toString();
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out non-null; where to print to
+     */
+    public void printContext(PrintStream out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out non-null; where to print to
+     */
+    public void printContext(PrintWriter out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+}
diff --git a/dx/src/com/android/dx/util/FileUtils.java b/dx/src/com/android/dx/util/FileUtils.java
new file mode 100644
index 0000000..07a7c7e
--- /dev/null
+++ b/dx/src/com/android/dx/util/FileUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+    /**
+     * This class is uninstantiable.
+     */
+    private FileUtils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Reads the named file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     * 
+     * @param fileName non-null; name of the file to read
+     * @return non-null; contents of the file
+     */
+    public static byte[] readFile(String fileName) {
+        File file = new File(fileName);
+        return readFile(file);
+    }
+
+    /**
+     * Reads the given file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     * 
+     * @param file non-null; the file to read
+     * @return non-null; contents of the file
+     */
+    public static byte[] readFile(File file) {
+        if (!file.exists()) {
+            throw new RuntimeException(file + ": file not found");
+        }
+
+        if (!file.isFile()) {
+            throw new RuntimeException(file + ": not a file");
+        }
+
+        if (!file.canRead()) {
+            throw new RuntimeException(file + ": file not readable");
+        }
+
+        long longLength = file.length();
+        int length = (int) longLength;
+        if (length != longLength) {
+            throw new RuntimeException(file + ": file too long");
+        }
+
+        byte[] result = new byte[length];
+
+        try {
+            FileInputStream in = new FileInputStream(file);
+            int at = 0;
+            while (length > 0) {
+                int amt = in.read(result, at, length);
+                if (amt == -1) {
+                    throw new RuntimeException(file + ": unexpected EOF");
+                }
+                at += amt;
+                length -= amt;
+            }
+            in.close();
+        } catch (IOException ex) {
+            throw new RuntimeException(file + ": trouble reading", ex);
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/util/FixedSizeList.java b/dx/src/com/android/dx/util/FixedSizeList.java
new file mode 100644
index 0000000..7b7d325
--- /dev/null
+++ b/dx/src/com/android/dx/util/FixedSizeList.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple (mostly) fixed-size list of objects, which may be made immutable.
+ */
+public class FixedSizeList
+        extends MutabilityControl implements ToHuman {
+    /** non-null; array of elements */
+    private Object[] arr;
+
+    /**
+     * Constructs an instance. All indices initially contain <code>null</code>.
+     * 
+     * @param size the size of the list
+     */
+    public FixedSizeList(int size) {
+        super(size != 0);
+
+        try {
+            arr = new Object[size];
+        } catch (NegativeArraySizeException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("size < 0");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            // Easy out.
+            return true;
+        }
+
+        if ((other == null) || (getClass() != other.getClass())) {
+            // Another easy out.
+            return false;
+        }
+
+        FixedSizeList list = (FixedSizeList) other;
+        return Arrays.equals(arr, list.arr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(arr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        String name = getClass().getName();
+
+        return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+                         ", ",
+                         "}",
+                         false);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * This method will only work if every element of the list
+     * implements {@link ToHuman}.
+     */
+    public String toHuman() {
+        String name = getClass().getName();
+
+        return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+                         ", ",
+                         "}",
+                         true);
+    }
+
+    /**
+     * Gets a customized string form for this instance.
+     * 
+     * @param prefix null-ok; prefix for the start of the result
+     * @param separator null-ok; separator to insert between each item
+     * @param suffix null-ok; suffix for the end of the result
+     * @return non-null; the custom string
+     */
+    public String toString(String prefix, String separator, String suffix) {
+        return toString0(prefix, separator, suffix, false);
+    }
+
+    /**
+     * Gets a customized human string for this instance. This method will
+     * only work if every element of the list implements {@link
+     * ToHuman}.
+     * 
+     * @param prefix null-ok; prefix for the start of the result
+     * @param separator null-ok; separator to insert between each item
+     * @param suffix null-ok; suffix for the end of the result
+     * @return non-null; the custom string
+     */
+    public String toHuman(String prefix, String separator, String suffix) {
+        return toString0(prefix, separator, suffix, true);
+    }
+
+    /**
+     * Gets the number of elements in this list.
+     */
+    public final int size() {
+        return arr.length;
+    }
+
+    /**
+     * Shrinks this instance to fit, by removing any unset
+     * (<code>null</code>) elements, leaving the remaining elements in
+     * their original order.
+     */
+    public void shrinkToFit() {
+        int sz = arr.length;
+        int newSz = 0;
+
+        for (int i = 0; i < sz; i++) {
+            if (arr[i] != null) {
+                newSz++;
+            }
+        }
+
+        if (sz == newSz) {
+            return;
+        }
+
+        throwIfImmutable();
+
+        Object[] newa = new Object[newSz];
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Object one = arr[i];
+            if (one != null) {
+                newa[at] = one;
+                at++;
+            }
+        }
+
+        arr = newa;
+        if (newSz == 0) {
+            setImmutable();
+        }
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw <code>NullPointerException</code>. This method is
+     * protected so that subclasses may offer a safe type-checked
+     * public interface to their clients.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return non-null; the indicated element
+     */
+    protected final Object get0(int n) {
+        try {
+            Object result = arr[n];
+
+            if (result == null) {
+                throw new NullPointerException("unset: " + n);
+            }
+
+            return result;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwIndex(n);
+        }
+    }
+
+    /**
+     * Gets the indicated element, allowing <code>null</code>s to be
+     * returned. This method is protected so that subclasses may
+     * (optionally) offer a safe type-checked public interface to
+     * their clients.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return null-ok; the indicated element
+     */
+    protected final Object getOrNull0(int n) {
+        return arr[n];
+    }
+
+    /**
+     * Sets the element at the given index, but without doing any type
+     * checks on the element. This method is protected so that
+     * subclasses may offer a safe type-checked public interface to
+     * their clients.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param obj null-ok; the value to store
+     */
+    protected final void set0(int n, Object obj) {
+        throwIfImmutable();
+
+        try {
+            arr[n] = obj;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throwIndex(n);
+        }
+    }
+
+    /**
+     * Throws the appropriate exception for the given index value.
+     * 
+     * @param n the index value
+     * @return never
+     * @throws IndexOutOfBoundsException always thrown
+     */
+    private Object throwIndex(int n) {
+        if (n < 0) {
+            throw new IndexOutOfBoundsException("n < 0");
+        }
+
+        throw new IndexOutOfBoundsException("n >= size()");
+    }
+
+    /**
+     * Helper for {@link #toString} and {@link #toHuman}, which both of
+     * those call to pretty much do everything.
+     * 
+     * @param prefix null-ok; prefix for the start of the result
+     * @param separator null-ok; separator to insert between each item
+     * @param suffix null-ok; suffix for the end of the result
+     * @param human whether the output is to be human 
+     * @return non-null; the custom string
+     */
+    private String toString0(String prefix, String separator, String suffix,
+                             boolean human) {
+        int len = arr.length;
+        StringBuffer sb = new StringBuffer(len * 10 + 10);
+
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+
+        for (int i = 0; i < len; i++) {
+            if ((i != 0) && (separator != null)) {
+                sb.append(separator);
+            }
+
+            if (human) {
+                sb.append(((ToHuman) arr[i]).toHuman());
+            } else {
+                sb.append(arr[i]);
+            }
+        }
+
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+
+        return sb.toString();
+    }
+
+}
diff --git a/dx/src/com/android/dx/util/Hex.java b/dx/src/com/android/dx/util/Hex.java
new file mode 100644
index 0000000..cf4c130
--- /dev/null
+++ b/dx/src/com/android/dx/util/Hex.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for formatting numbers as hexadecimal.
+ */
+public final class Hex {
+    /**
+     * This class is uninstantiable.
+     */
+    private Hex() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Formats a <code>long</code> as an 8-byte unsigned hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u8(long v) {
+        char[] result = new char[16];
+        for (int i = 0; i < 16; i++) {
+            result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 4-byte unsigned hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u4(int v) {
+        char[] result = new char[8];
+        for (int i = 0; i < 8; i++) {
+            result[7 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 3-byte unsigned hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u3(int v) {
+        char[] result = new char[6];
+        for (int i = 0; i < 6; i++) {
+            result[5 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 2-byte unsigned hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u2(int v) {
+        char[] result = new char[4];
+        for (int i = 0; i < 4; i++) {
+            result[3 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as either a 2-byte unsigned hex value
+     * (if the value is small enough) or a 4-byte unsigned hex value (if
+     * not).
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u2or4(int v) {
+        if (v == (char) v) {
+            return u2(v);
+        } else {
+            return u4(v);
+        }
+    }
+
+    /**
+     * Formats an <code>int</code> as a 1-byte unsigned hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String u1(int v) {
+        char[] result = new char[2];
+        for (int i = 0; i < 2; i++) {
+            result[1 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 4-bit unsigned hex nibble.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String uNibble(int v) {
+        char[] result = new char[1];
+
+        result[0] = Character.forDigit(v & 0x0f, 16);
+        return new String(result);
+    }
+
+    /**
+     * Formats a <code>long</code> as an 8-byte signed hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String s8(long v) {
+        char[] result = new char[17];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 16; i++) {
+            result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 4-byte signed hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String s4(int v) {
+        char[] result = new char[9];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 8; i++) {
+            result[8 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 2-byte signed hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String s2(int v) {
+        char[] result = new char[5];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 4; i++) {
+            result[4 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an <code>int</code> as a 1-byte signed hex value.
+     * 
+     * @param v value to format
+     * @return non-null; formatted form
+     */
+    public static String s1(int v) {
+        char[] result = new char[3];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 2; i++) {
+            result[2 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats a hex dump of a portion of a <code>byte[]</code>. The result
+     * is always newline-terminated, unless the passed-in length was zero,
+     * in which case the result is always the empty string (<code>""</code>).
+     * 
+     * @param arr non-null; array to format
+     * @param offset &gt;= 0; offset to the part to dump
+     * @param length &gt;= 0; number of bytes to dump
+     * @param outOffset &gt;= 0; first output offset to print
+     * @param bpl &gt;= 0; number of bytes of output per line
+     * @param addressLength {2,4,6,8}; number of characters for each address
+     * header
+     * @return non-null; a string of the dump
+     */
+    public static String dump(byte[] arr, int offset, int length,
+                              int outOffset, int bpl, int addressLength) {
+        int end = offset + length;
+
+        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+        if (((offset | length | end) < 0) || (end > arr.length)) {
+            throw new IndexOutOfBoundsException("arr.length " +
+                                                arr.length + "; " + 
+                                                offset + "..!" + end);
+        }
+
+        if (outOffset < 0) {
+            throw new IllegalArgumentException("outOffset < 0");
+        }
+
+        if (length == 0) {
+            return "";
+        }
+
+        StringBuffer sb = new StringBuffer(length * 4 + 6);
+        boolean bol = true;
+        int col = 0;
+
+        while (length > 0) {
+            if (col == 0) {
+                String astr;
+                switch (addressLength) {
+                    case 2:  astr = Hex.u1(outOffset); break;
+                    case 4:  astr = Hex.u2(outOffset); break;
+                    case 6:  astr = Hex.u3(outOffset); break;
+                    default: astr = Hex.u4(outOffset); break;
+                }
+                sb.append(astr);
+                sb.append(": ");
+            } else if ((col & 1) == 0) {
+                sb.append(' ');
+            }
+            sb.append(Hex.u1(arr[offset]));
+            outOffset++;
+            offset++;
+            col++;
+            if (col == bpl) {
+                sb.append('\n');
+                col = 0;
+            }
+            length--;
+        }
+
+        if (col != 0) {
+            sb.append('\n');
+        }
+
+        return sb.toString();        
+    }
+}
diff --git a/dx/src/com/android/dx/util/HexParser.java b/dx/src/com/android/dx/util/HexParser.java
new file mode 100644
index 0000000..4b6b7b2
--- /dev/null
+++ b/dx/src/com/android/dx/util/HexParser.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for parsing hexadecimal text.
+ */
+public final class HexParser {
+    /**
+     * This class is uninstantiable.
+     */
+    private HexParser() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Parses the given text as hex, returning a <code>byte[]</code>
+     * corresponding to the text. The format is simple: Each line may
+     * start with a hex offset followed by a colon (which is verified
+     * and presumably used just as a comment), and then consists of
+     * hex digits freely interspersed with whitespace. If a pound sign
+     * is encountered, it and the rest of the line are ignored as a
+     * comment. If a double quote is encountered, then the ASCII value
+     * of the subsequent characters is used, until the next double
+     * quote. Quoted strings may not span multiple lines.
+     * 
+     * @param src non-null; the source string
+     * @return non-null; the parsed form
+     */
+    public static byte[] parse(String src) {
+        int len = src.length();
+        byte[] result = new byte[len / 2];
+        int at = 0;
+        int outAt = 0;
+
+        while (at < len) {
+            int nlAt = src.indexOf('\n', at);
+            if (nlAt < 0) {
+                nlAt = len;
+            }
+            int poundAt = src.indexOf('#', at);
+
+            String line;
+            if ((poundAt >= 0) && (poundAt < nlAt)) {
+                line = src.substring(at, poundAt);
+            } else {
+                line = src.substring(at, nlAt);
+            }
+            at = nlAt + 1;
+
+            int colonAt = line.indexOf(':');
+
+            atCheck:
+            if (colonAt != -1) {
+                int quoteAt = line.indexOf('\"');
+                if ((quoteAt != -1) && (quoteAt < colonAt)) {
+                    break atCheck;
+                }
+
+                String atStr = line.substring(0, colonAt).trim();
+                line = line.substring(colonAt + 1);
+                int alleged = Integer.parseInt(atStr, 16);
+                if (alleged != outAt) {
+                    throw new RuntimeException("bogus offset marker: " +
+                                               atStr);
+                }
+            }
+
+            int lineLen = line.length();
+            int value = -1;
+            boolean quoteMode = false;
+
+            for (int i = 0; i < lineLen; i++) {
+                char c = line.charAt(i);
+
+                if (quoteMode) {
+                    if (c == '\"') {
+                        quoteMode = false;
+                    } else {
+                        result[outAt] = (byte) c;
+                        outAt++;
+                    }
+                    continue;
+                }
+
+                if (c <= ' ') {
+                    continue;
+                }
+                if (c == '\"') {
+                    if (value != -1) {
+                        throw new RuntimeException("spare digit around " +
+                                                   "offset " + Hex.u4(outAt));
+                    }
+                    quoteMode = true;
+                    continue;
+                }
+
+                int digVal = Character.digit(c, 16);
+                if (digVal == -1) {
+                    throw new RuntimeException("bogus digit character: \"" +
+                                               c + "\"");
+                }
+                if (value == -1) {
+                    value = digVal;
+                } else {
+                    result[outAt] = (byte) ((value << 4) | digVal);
+                    outAt++;
+                    value = -1;
+                }
+            }
+
+            if (value != -1) {
+                throw new RuntimeException("spare digit around offset " +
+                                           Hex.u4(outAt));
+            }
+
+            if (quoteMode) {
+                throw new RuntimeException("unterminated quote around " +
+                                           "offset " + Hex.u4(outAt));
+            }
+        }
+
+        if (outAt < result.length) {
+            byte[] newr = new byte[outAt];
+            System.arraycopy(result, 0, newr, 0, outAt);
+            result = newr;
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IndentingWriter.java b/dx/src/com/android/dx/util/IndentingWriter.java
new file mode 100644
index 0000000..db4e0a2
--- /dev/null
+++ b/dx/src/com/android/dx/util/IndentingWriter.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that wraps another writer and passes width-limited and
+ * optionally-prefixed output to its subordinate. When lines are
+ * wrapped they are automatically indented based on the start of the
+ * line.
+ */
+public final class IndentingWriter extends FilterWriter {
+    /** null-ok; optional prefix for every line */
+    private final String prefix;
+
+    /** &gt; 0; the maximum output width */
+    private final int width;
+
+    /** &gt; 0; the maximum indent */
+    private final int maxIndent;
+
+    /** &gt;= 0; current output column (zero-based) */
+    private int column;
+
+    /** whether indent spaces are currently being collected */
+    private boolean collectingIndent;
+
+    /** &gt;= 0; current indent amount */
+    private int indent;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param out non-null; writer to send final output to
+     * @param width &gt;= 0; the maximum output width (not including
+     * <code>prefix</code>), or <code>0</code> for no maximum
+     * @param prefix non-null; the prefix for each line
+     */
+    public IndentingWriter(Writer out, int width, String prefix) {
+        super(out);
+
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        if (width < 0) {
+            throw new IllegalArgumentException("width < 0");
+        }
+
+        if (prefix == null) {
+            throw new NullPointerException("prefix == null");
+        }
+
+        this.width = (width != 0) ? width : Integer.MAX_VALUE;
+        this.maxIndent = width >> 1;
+        this.prefix = (prefix.length() == 0) ? null : prefix;
+
+        bol();
+    }
+
+    /**
+     * Constructs a no-prefix instance.
+     * 
+     * @param out non-null; writer to send final output to
+     * @param width &gt;= 0; the maximum output width (not including
+     * <code>prefix</code>), or <code>0</code> for no maximum
+     */
+    public IndentingWriter(Writer out, int width) {
+        this(out, width, "");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(int c) throws IOException {
+        synchronized (lock) {
+            if (collectingIndent) {
+                if (c == ' ') {
+                    indent++;
+                    if (indent >= maxIndent) {
+                        indent = maxIndent;
+                        collectingIndent = false;
+                    }
+                } else {
+                    collectingIndent = false;
+                }
+            }
+
+            if ((column == width) && (c != '\n')) {
+                out.write('\n');
+                column = 0;
+                /*
+                 * Note: No else, so this should fall through to the next
+                 * if statement.
+                 */
+            }
+
+            if (column == 0) {
+                if (prefix != null) {
+                    out.write(prefix);
+                }
+
+                if (!collectingIndent) {
+                    for (int i = 0; i < indent; i++) {
+                        out.write(' ');
+                    }
+                    column = indent;
+                }
+            }
+
+            out.write(c);
+
+            if (c == '\n') {
+                bol();
+            } else {
+                column++;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        synchronized (lock) {
+            while (len > 0) {
+                write(cbuf[off]);
+                off++;
+                len--;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(String str, int off, int len) throws IOException {
+        synchronized (lock) {
+            while (len > 0) {
+                write(str.charAt(off));
+                off++;
+                len--;
+            }
+        }
+    }
+
+    /**
+     * Indicates that output is at the beginning of a line.
+     */
+    private void bol() {
+        column = 0;
+        collectingIndent = (maxIndent != 0);
+        indent = 0;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IntIterator.java b/dx/src/com/android/dx/util/IntIterator.java
new file mode 100644
index 0000000..88181b5
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntIterator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An iterator for a list of ints.
+ */
+public interface IntIterator {
+
+    /**
+     * Checks to see if the iterator has a next value.
+     *
+     * @return true if next() will succeed
+     */
+    boolean hasNext();
+
+    /**
+     * Returns the next value in the iterator.
+     *
+     * @return next value
+     * @throws java.util.NoSuchElementException if no next element exists
+     */
+    int next();
+
+    /**
+     * Removes a value from the collection underlying this iterator.
+     * May throw UnsupportedOperationException().
+     */
+//    void remove();
+}
diff --git a/dx/src/com/android/dx/util/IntList.java b/dx/src/com/android/dx/util/IntList.java
new file mode 100644
index 0000000..f60bbb5
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntList.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple list of <code>int</code>s.
+ */
+public final class IntList extends MutabilityControl {
+    /** non-null; immutable, no-element instance */
+    public static final IntList EMPTY = new IntList(0);
+
+    /** non-null; array of elements */
+    private int[] values;
+
+    /** &gt;= 0; current size of the list */
+    private int size;
+
+    /** whether the values are currently sorted */
+    private boolean sorted;
+
+    static {
+        EMPTY.setImmutable();
+    }
+
+    /**
+     * Constructs a new immutable instance with the given element.
+     * 
+     * @param value the sole value in the list
+     */
+    public static IntList makeImmutable(int value) {
+        IntList result = new IntList(1);
+
+        result.add(value);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs a new immutable instance with the given elements.
+     * 
+     * @param value0 the first value in the list
+     * @param value1 the second value in the list
+     */
+    public static IntList makeImmutable(int value0, int value1) {
+        IntList result = new IntList(2);
+
+        result.add(value0);
+        result.add(value1);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs an empty instance with a default initial capacity.
+     */
+    public IntList() {
+        this(4);
+    }
+
+    /**
+     * Constructs an empty instance.
+     * 
+     * @param initialCapacity &gt;= 0; initial capacity of the list
+     */
+    public IntList(int initialCapacity) {
+        super(true);
+
+        try {
+            values = new int[initialCapacity];
+        } catch (NegativeArraySizeException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("size < 0");
+        }
+
+        size = 0;
+        sorted = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int result = 0;
+
+        for (int i = 0; i < size; i++) {
+            result = (result * 31) + values[i];
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+
+        if (! (other instanceof IntList)) {
+            return false;
+        }
+
+        IntList otherList = (IntList) other;
+
+        if (sorted != otherList.sorted) {
+            return false;
+        }
+
+        if (size != otherList.size) {
+            return false;
+        }
+
+        for (int i = 0; i < size; i++) {
+            if (values[i] != otherList.values[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(size * 5 + 10);
+
+        sb.append('{');
+
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(values[i]);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the number of elements in this list.
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Gets the indicated value.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @return the indicated element's value
+     */
+    public int get(int n) {
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        try {
+            return values[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate exception.
+            throw new IndexOutOfBoundsException("n < 0");
+        }
+    }
+
+    /**
+     * Sets the value at the given index.
+     * 
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param value value to store
+     */
+    public void set(int n, int value) {
+        throwIfImmutable();
+
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        try {
+            values[n] = value;
+            sorted = false;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            if (n < 0) {
+                throw new IllegalArgumentException("n < 0");
+            }
+        }
+    }
+
+    /**
+     * Adds an element to the end of the list. This will increase the
+     * list's capacity if necessary.
+     * 
+     * @param value the value to add
+     */
+    public void add(int value) {
+        throwIfImmutable();
+
+        growIfNeeded();
+
+        values[size++] = value;
+
+        if (sorted && (size > 1)) {
+            sorted = (value >= values[size - 2]);
+        }
+    }
+
+    /**
+     * Inserts element into specified index, moving elements at and above
+     * that index up one. May not be used to insert at an index beyond the
+     * current size (that is, insertion as a last element is legal but
+     * no further).
+     *
+     * @param n &gt=0 &lt=size(); index of where to insert
+     * @param value value to insert
+     */
+    public void insert(int n, int value) {
+        if (n > size) {
+            throw new IndexOutOfBoundsException("n > size()");
+        }
+
+        growIfNeeded();
+
+        System.arraycopy (values, n, values, n+1, size - n);
+        values[n] = value;
+        size++;
+
+        sorted = sorted
+                && (n == 0 || value > values[n-1])
+                && (n == (size - 1) || value < values[n+1]);
+    }
+
+    /**
+     * Removes an element at a given index, shifting elements at greater
+     * indicies down one.
+     *
+     * @param n  &gt=0 &lt size(); index of element to remove
+     */
+    public void removeIndex(int n) {
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        System.arraycopy (values, n + 1, values, n, size - n - 1);
+        size--;
+
+        // sort status is unchanged
+    }
+
+    /**
+     * Increases size of array if needed
+     */
+    private void growIfNeeded() {
+        if (size == values.length) {
+            // Resize.
+            int[] newv = new int[size * 3 / 2 + 10];
+            System.arraycopy(values, 0, newv, 0, size);
+            values = newv;
+        }
+    }
+
+    /**
+     * Returns the last element in the array without modifying the array
+     *
+     * @return last value in the array.
+     * @exception IndexOutOfBoundsException if stack is empty.
+     */
+    public int top() {
+        return get(size - 1);
+    }
+
+    /**
+     * Pops an element off the end of the list and decreasing the size by one.
+     *
+     * @return value from what was the last element.
+     * @exception IndexOutOfBoundsException if stack is empty.
+     */
+    public int pop() {
+        throwIfImmutable();
+
+        int result;
+
+        result = get(size-1);
+        size--;
+
+        return result;    
+    }
+
+    /**
+     * Pops N elements off the end of the list and decreasing the size by N.
+     *
+     * @param n &gt;= 0; number of elements to remove from end.
+     * @exception IndexOutOfBoundsException if stack is smaller than N
+     */
+    public void pop(int n) {
+        throwIfImmutable();
+
+        size -= n;
+    }
+
+    /**
+     * Shrinks the size of the list.
+     * 
+     * @param newSize &gt;= 0; the new size
+     */
+    public void shrink(int newSize) {
+        if (newSize < 0) {
+            throw new IllegalArgumentException("newSize < 0");
+        }
+
+        if (newSize > size) {
+            throw new IllegalArgumentException("newSize > size");
+        }
+
+        throwIfImmutable();
+
+        size = newSize;
+    }
+
+    /**
+     * Makes and returns a mutable copy of the list.
+     * 
+     * @return non-null; an appropriately-constructed instance
+     */
+    public IntList mutableCopy() {
+        int sz = size;
+        IntList result = new IntList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.add(values[i]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Sorts the elements in the list in-place.
+     */
+    public void sort() {
+        throwIfImmutable();
+
+        if (!sorted) {
+            Arrays.sort(values, 0, size);
+            sorted = true;
+        }
+    }
+
+    /**
+     * Returns the index of the given value, or -1 if the value does not
+     * appear in the list.  This will do a binary search if the list is
+     * sorted or a linear search if not.
+     * @param value value to find
+     * @return index of value or -1
+     */
+    public int indexOf(int value) {
+        int ret = binarysearch(value);
+
+        return ret >= 0 ? ret : -1;
+
+    }
+
+    /**
+     * Performs a binary search on a sorted list, returning the index of
+     * the given value if it is present or
+     * <code>(-(insertion point) - 1)</code> if the value is not present.
+     * If the list is not sorted, then reverts to linear search and returns
+     * <code>-size()</code> if the element is not found.
+     *
+     * @param value value to find
+     * @return index of value or <code>(-(insertion point) - 1)</code> if the
+     * value is not present
+     */
+    public int binarysearch(int value) {
+        int sz = size;
+
+        if (!sorted) {
+            // Linear search.
+            for (int i = 0; i < sz; i++) {
+                if (values[i] == value) {
+                    return i;
+                }
+            }
+
+            return -sz;
+        }
+
+        /*
+         * Binary search. This variant does only one value comparison
+         * per iteration but does one more iteration on average than
+         * the variant that includes a value equality check per
+         * iteration.
+         */
+
+        int min = -1;
+        int max = sz;
+
+        while (max > (min + 1)) {
+            /*
+             * The guessIdx calculation is equivalent to ((min + max)
+             * / 2) but won't go wonky when min and max are close to
+             * Integer.MAX_VALUE.
+             */
+            int guessIdx = min + ((max - min) >> 1);
+            int guess = values[guessIdx];
+
+            if (value <= guess) {
+                max = guessIdx;
+            } else {
+                min = guessIdx;
+            }
+        }
+
+        if ((max != sz)) {
+            return (value == values[max]) ? max : (-max - 1);
+        } else {
+            return -sz - 1;
+        }
+    }
+
+
+    /**
+     * Returns whether or not the given value appears in the list.
+     * This will do a binary search if the list is sorted or a linear
+     * search if not.
+     * 
+     * @see #sort
+     * 
+     * @param value value to look for
+     * @return whether the list contains the given value
+     */
+    public boolean contains(int value) {
+        return indexOf(value) >= 0;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IntSet.java b/dx/src/com/android/dx/util/IntSet.java
new file mode 100644
index 0000000..10b6ee0
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntSet.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * A set of integers
+ */
+public interface IntSet {
+
+    /**
+     * Adds an int to a set
+     *
+     * @param value int to add
+     */
+    void add(int value);
+
+    /**
+     * Removes an int from a set.
+     *
+     * @param value int to remove
+     */
+    void remove(int value);
+
+    /**
+     * Checks to see if a value is in the set
+     *
+     * @param value int to check
+     * @return true if in set
+     */
+    boolean has(int value);
+
+    /**
+     * Merges <code>other</code> into this set, so this set becomes the
+     * union of the two.
+     *
+     * @param other non-null; other set to merge with.
+     */
+    void merge(IntSet other);
+
+    /**
+     * Returns the count of unique elements in this set.
+     *
+     * @return &gt; = 0; count of unique elements
+     */
+    int elements();
+
+    /**
+     * Iterates the set
+     *
+     * @return non-null; a set iterator
+     */
+    IntIterator iterator();
+}
diff --git a/dx/src/com/android/dx/util/LabeledItem.java b/dx/src/com/android/dx/util/LabeledItem.java
new file mode 100644
index 0000000..cc6a0d2
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledItem.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An item that has an integer label.
+ */
+public interface LabeledItem {
+
+    /*
+     * Gets the label of this block.
+     *
+     * @return &gt;= 0; the label
+     */
+    public int getLabel();
+}
diff --git a/dx/src/com/android/dx/util/LabeledList.java b/dx/src/com/android/dx/util/LabeledList.java
new file mode 100644
index 0000000..3168a38
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledList.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import com.android.dx.cf.code.ByteBlock;
+
+/**
+ * A list of labeled items, allowing easy lookup by label.
+ */
+public class LabeledList extends FixedSizeList {
+
+    /**
+     * Sparse array indexed by label to FixedSizeList index.
+     * -1 = invalid label.
+     */
+    private final IntList labelToIndex;
+
+    /** @inheritDoc */
+    public LabeledList(int size) {
+        super(size);
+
+        labelToIndex = new IntList(size);
+    }
+
+    /**
+     * Constructs a new instance that is a copy of the old instance.
+     *
+     * @param old instance to copy
+     */
+    protected LabeledList(LabeledList old) {
+        super(old.size());
+        labelToIndex = old.labelToIndex.mutableCopy();
+
+        int sz = old.size();
+
+        for (int i = 0; i < sz; i++) {
+            Object one = old.get0(i);
+            if (one != null) {
+                set0(i, one);
+            }
+        }
+    }
+
+    /**
+     * Gets the maximum label (exclusive) of any block added to this instance.
+     *
+     * @return &gt;= 0; the maximum label
+     */
+    public int getMaxLabel() {
+        int sz = labelToIndex.size();
+
+        // Gobble any deleted labels that may be at the end...
+        int i;
+        for (i = sz - 1; (i >= 0) && (labelToIndex.get(i) < 0); i--)
+            ;
+
+        int newSize = i+1;
+
+        labelToIndex.shrink(newSize);
+
+        return newSize;
+    }
+
+    /**
+     * Removes a label from the label-to-index mapping
+     * @param oldLabel label to remove
+     */
+    protected void removeLabel(int oldLabel) {
+        labelToIndex.set(oldLabel, -1);
+    }
+
+    /**
+     * Adds a label and index to the label-to-index mapping
+     * @param label new label
+     * @param index index of block.
+     */
+    protected void addLabelIndex(int label, int index) {
+        int origSz = labelToIndex.size();
+
+        for (int i = 0; i <= (label - origSz); i++) {
+            labelToIndex.add(-1);
+        }
+
+        labelToIndex.set(label, index);
+    }
+
+    /**
+     * Gets the index of the first item in the list with the given
+     * label, if any.
+     *
+     * @param label &gt;= 0; the label to look for
+     * @return &gt;= -1; the index of the so-labelled item, or <code>-1</code>
+     * if none is found
+     */
+    public int indexOfLabel(int label) {
+        if (label >= labelToIndex.size()) {
+            return -1;
+        } else {
+            return labelToIndex.get(label);
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void shrinkToFit() {
+        super.shrinkToFit();
+
+        rebuildLabelToIndex();
+    }
+
+    /**
+     * Rebuilds the label-to-index mapping after a shrinkToFit().
+     * Note: assumes that the labels that are in the list are the same
+     * although the indicies may have changed.
+     */
+    protected void rebuildLabelToIndex() {
+        int szItems = size();
+
+        for (int i = 0; i < szItems; i++) {
+            LabeledItem li = (LabeledItem)get0(i);
+
+            if (li != null) {
+                labelToIndex.set(li.getLabel(), i);
+            }
+        }
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param n &gt;= 0, &lt; size(); which element
+     * @param item null-ok; the value to store
+     */
+    protected void set(int n, LabeledItem item) {
+        LabeledItem old = (LabeledItem) getOrNull0(n);
+
+        set0(n, item);
+
+        if (old != null) {
+            removeLabel(old.getLabel());
+        }
+
+        if (item != null) {
+            addLabelIndex(item.getLabel(), n);
+        }        
+    }
+}
diff --git a/dx/src/com/android/dx/util/Leb128Utils.java b/dx/src/com/android/dx/util/Leb128Utils.java
new file mode 100644
index 0000000..dfd416f
--- /dev/null
+++ b/dx/src/com/android/dx/util/Leb128Utils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * LEB128 (little-endian base 128) utilities.
+ */
+public final class Leb128Utils {
+    /**
+     * This class is uninstantiable.
+     */
+    private Leb128Utils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the number of bytes in the unsigned LEB128 encoding of the
+     * given value.
+     * 
+     * @param value the value in question
+     * @return its write size, in bytes
+     */
+    public static int unsignedLeb128Size(int value) {
+        // TODO: This could be much cleverer.
+        
+        int remaining = value >> 7;
+        int count = 0;
+
+        while (remaining != 0) {
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        return count + 1;
+    }
+
+    /**
+     * Gets the number of bytes in the signed LEB128 encoding of the
+     * given value.
+     * 
+     * @param value the value in question
+     * @return its write size, in bytes
+     */
+    public static int signedLeb128Size(int value) {
+        // TODO: This could be much cleverer.
+
+        int remaining = value >> 7;
+        int count = 0;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                || ((remaining & 1) != ((value >> 6) & 1));
+
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        return count;
+    }
+}
diff --git a/dx/src/com/android/dx/util/ListIntSet.java b/dx/src/com/android/dx/util/ListIntSet.java
new file mode 100644
index 0000000..a9f79af
--- /dev/null
+++ b/dx/src/com/android/dx/util/ListIntSet.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a list
+ */
+public class ListIntSet implements IntSet {
+
+    /** also accessed in BitIntSet */
+    final IntList ints;
+
+    /**
+     * Constructs an instance
+     */
+    public ListIntSet() {
+        ints = new IntList();
+        ints.sort();
+    }
+
+    /** @inheritDoc */
+    public void add(int value) {
+        int index = ints.binarysearch(value);
+
+        if (index < 0) {
+            ints.insert(-(index + 1), value);
+        }
+    }
+
+    /** @inheritDoc */
+    public void remove(int value) {
+        int index = ints.indexOf(value);
+
+        if (index >= 0) {
+            ints.removeIndex(index);
+        }
+    }
+
+    /** @inheritDoc */
+    public boolean has(int value) {
+        return ints.indexOf(value) >= 0;    
+    }
+
+    /** @inheritDoc */
+    public void merge(IntSet other) {
+        if (other instanceof ListIntSet) {
+            ListIntSet o = (ListIntSet) other;
+            int szThis = ints.size();
+            int szOther = o.ints.size();
+
+            int i = 0;
+            int j = 0;
+
+            while (j < szOther && i < szThis) {
+                while (j < szOther && o.ints.get(j) < ints.get(i)) {
+                    add(o.ints.get(j++));
+                }                
+                if (j == szOther) {
+                    break;
+                }
+                while (i < szThis && o.ints.get(j) >= ints.get(i)) {
+                    i++;
+                }
+            }
+
+            while (j < szOther) {
+                add(o.ints.get(j++));
+            }
+
+            ints.sort();
+        } else if (other instanceof BitIntSet) {
+            BitIntSet o = (BitIntSet) other;
+
+            for (int i = 0; i >= 0; i = Bits.findFirst(o.bits, i + 1)) {
+                ints.add(i);
+            }
+            ints.sort();
+        } else {
+            IntIterator iter = other.iterator();
+            while (iter.hasNext()) {
+                add(iter.next());
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int elements() {
+        return ints.size();
+    }
+
+    /** @inheritDoc */
+    public IntIterator iterator() {
+        return new IntIterator() {
+            private int idx = 0;
+
+            /** @inheritDoc */
+            public boolean hasNext() {
+                return idx < ints.size();
+            }
+
+            /** @inheritDoc */
+            public int next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                return ints.get(idx++);
+            }
+
+            /** @inheritDoc */
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /** @inheritDoc */
+    public String toString() {
+        return ints.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityControl.java b/dx/src/com/android/dx/util/MutabilityControl.java
new file mode 100644
index 0000000..8b3383b
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityControl.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Very simple base class that implements a flag to control the mutability
+ * of instances. This class just provides the flag and a utility to check
+ * and throw the right exception, but it is up to subclasses to place calls
+ * to the checker in all the right places.
+ */
+public class MutabilityControl {
+    /** whether this instance is mutable */
+    private boolean mutable;
+
+    /**
+     * Constructs an instance. It is initially mutable.
+     */
+    public MutabilityControl() {
+        mutable = true;
+    }
+
+    /**
+     * Constructs an instance, explicitly indicating the mutability.
+     *
+     * @param mutable <code>true</code> iff this instance is mutable
+     */
+    public MutabilityControl(boolean mutable) {
+        this.mutable = mutable;
+    }
+
+    /**
+     * Makes this instance immutable.
+     */
+    public void setImmutable() {
+        mutable = false;
+    }
+
+    /**
+     * Checks to see whether or not this instance is immutable. This is the
+     * same as calling <code>!isMutable()</code>.
+     *
+     * @return <code>true</code> iff this instance is immutable
+     */
+    public final boolean isImmutable() {
+        return !mutable;
+    }
+
+    /**
+     * Checks to see whether or not this instance is mutable.
+     *
+     * @return <code>true</code> iff this instance is mutable
+     */
+    public final boolean isMutable() {
+        return mutable;
+    }
+
+    /**
+     * Throws {@link MutabilityException} if this instance is
+     * immutable.
+     */
+    public final void throwIfImmutable() {
+        if (!mutable) {
+            throw new MutabilityException("immutable instance");
+        }
+    }
+
+    /**
+     * Throws {@link MutabilityException} if this instance is mutable.
+     */
+    public final void throwIfMutable() {
+        if (mutable) {
+            throw new MutabilityException("mutable instance");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityException.java b/dx/src/com/android/dx/util/MutabilityException.java
new file mode 100644
index 0000000..bd21651
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception due to a mutability problem.
+ */
+public class MutabilityException
+        extends ExceptionWithContext {
+    public MutabilityException(String message) {
+        super(message);
+    }
+
+    public MutabilityException(Throwable cause) {
+        super(cause);
+    }
+
+    public MutabilityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/util/Output.java b/dx/src/com/android/dx/util/Output.java
new file mode 100644
index 0000000..b3c3747
--- /dev/null
+++ b/dx/src/com/android/dx/util/Output.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a sink for binary output. This is similar to 
+ * <code>java.util.DataOutput</code>, but no <code>IOExceptions</code>
+ * are declared, and multibyte output is defined to be little-endian.
+ */
+public interface Output {
+    /**
+     * Gets the current cursor position. This is the same as the number of
+     * bytes written to this instance.
+     * 
+     * @return &gt;= 0; the cursor position
+     */
+    public int getCursor();
+
+    /**
+     * Asserts that the cursor is the given value.
+     * 
+     * @param expectedCursor the expected cursor value
+     * @throws RuntimeException thrown if <code>getCursor() !=
+     * expectedCursor</code>
+     */
+    public void assertCursor(int expectedCursor);
+ 
+    /**
+     * Writes a <code>byte</code> to this instance.
+     * 
+     * @param value the value to write; all but the low 8 bits are ignored
+     */
+    public void writeByte(int value);
+
+    /**
+     * Writes a <code>short</code> to this instance.
+     * 
+     * @param value the value to write; all but the low 16 bits are ignored
+     */
+    public void writeShort(int value);
+
+    /**
+     * Writes an <code>int</code> to this instance.
+     * 
+     * @param value the value to write
+     */
+    public void writeInt(int value);
+
+    /**
+     * Writes a <code>long</code> to this instance.
+     * 
+     * @param value the value to write
+     */
+    public void writeLong(long value);
+
+    /**
+     * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+     * see the "Dalvik Executable Format" document or DWARF v3 section
+     * 7.6.
+     *
+     * @param value value to write, treated as an unsigned value
+     * @return 1..5; the number of bytes actually written
+     */
+    public int writeUnsignedLeb128(int value);
+
+    /**
+     * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+     * see the "Dalvik Executable Format" document or DWARF v3 section
+     * 7.6.
+     *
+     * @param value value to write
+     * @return 1..5; the number of bytes actually written
+     */
+    public int writeSignedLeb128(int value);
+
+    /**
+     * Writes a {@link ByteArray} to this instance.
+     * 
+     * @param bytes non-null; the array to write
+     */
+    public void write(ByteArray bytes);
+
+    /**
+     * Writes a portion of a <code>byte[]</code> to this instance.
+     * 
+     * @param bytes non-null; the array to write
+     * @param offset &gt;= 0; offset into <code>bytes</code> for the first
+     * byte to write
+     * @param length &gt;= 0; number of bytes to write
+     */
+    public void write(byte[] bytes, int offset, int length);
+
+    /**
+     * Writes a <code>byte[]</code> to this instance. This is just
+     * a convenient shorthand for <code>write(bytes, 0, bytes.length)</code>.
+     * 
+     * @param bytes non-null; the array to write
+     */
+    public void write(byte[] bytes);
+
+    /** 
+     * Writes the given number of <code>0</code> bytes.
+     * 
+     * @param count &gt;= 0; the number of zeroes to write
+     */
+    public void writeZeroes(int count);
+
+    /** 
+     * Adds extra bytes if necessary (with value <code>0</code>) to
+     * force alignment of the output cursor as given.
+     * 
+     * @param alignment &gt; 0; the alignment; must be a power of two
+     */
+    public void alignTo(int alignment);
+}
diff --git a/dx/src/com/android/dx/util/ToHuman.java b/dx/src/com/android/dx/util/ToHuman.java
new file mode 100644
index 0000000..89bf4f7
--- /dev/null
+++ b/dx/src/com/android/dx/util/ToHuman.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Simple interface for objects that can return a "human" (as opposed to
+ * a complete but often hard to read) string form.
+ */
+public interface ToHuman {
+    /**
+     * Return the "human" string form of this instance.  This is
+     * generally less "debuggy" than <code>toString()</code>.
+     *
+     * @return non-null; the human string form
+     */
+    public String toHuman();
+}
diff --git a/dx/src/com/android/dx/util/TwoColumnOutput.java b/dx/src/com/android/dx/util/TwoColumnOutput.java
new file mode 100644
index 0000000..cc9f7d4
--- /dev/null
+++ b/dx/src/com/android/dx/util/TwoColumnOutput.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Class that takes a combined output destination and provides two
+ * output writers, one of which ends up writing to the left column and
+ * one which goes on the right.
+ */
+public final class TwoColumnOutput {
+    /** non-null; underlying writer for final output */
+    private final Writer out;
+
+    /** &gt; 0; the left column width */
+    private final int leftWidth;
+
+    /** non-null; pending left column output */
+    private final StringBuffer leftBuf;
+
+    /** non-null; pending right column output */
+    private final StringBuffer rightBuf;
+
+    /** non-null; left column writer */
+    private final IndentingWriter leftColumn;
+
+    /** non-null; right column writer */
+    private final IndentingWriter rightColumn;
+
+    /**
+     * Turns the given two strings (with widths) and spacer into a formatted
+     * two-column string.
+     * 
+     * @param s1 non-null; first string
+     * @param width1 &gt; 0; width of the first column
+     * @param spacer non-null; spacer string
+     * @param s2 non-null; second string
+     * @param width2 &gt; 0; width of the second column
+     * @return non-null; an appropriately-formatted string
+     */
+    public static String toString(String s1, int width1, String spacer,
+                                  String s2, int width2) {
+        int len1 = s1.length();
+        int len2 = s2.length();
+
+        StringWriter sw = new StringWriter((len1 + len2) * 3);
+        TwoColumnOutput twoOut =
+            new TwoColumnOutput(sw, width1, width2, spacer);
+
+        try {
+            twoOut.getLeft().write(s1);
+            twoOut.getRight().write(s2);
+        } catch (IOException ex) {
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        twoOut.flush();
+        return sw.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param out non-null; writer to send final output to
+     * @param leftWidth &gt; 0; width of the left column, in characters
+     * @param rightWidth &gt; 0; width of the right column, in characters
+     * @param spacer non-null; spacer string to sit between the two columns
+     */
+    public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
+                           String spacer) {
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        if (leftWidth < 1) {
+            throw new IllegalArgumentException("leftWidth < 1");
+        }
+
+        if (rightWidth < 1) {
+            throw new IllegalArgumentException("rightWidth < 1");
+        }
+
+        if (spacer == null) {
+            throw new NullPointerException("spacer == null");
+        }
+
+        StringWriter leftWriter = new StringWriter(1000);
+        StringWriter rightWriter = new StringWriter(1000);
+
+        this.out = out;
+        this.leftWidth = leftWidth;
+        this.leftBuf = leftWriter.getBuffer();
+        this.rightBuf = rightWriter.getBuffer();
+        this.leftColumn = new IndentingWriter(leftWriter, leftWidth);
+        this.rightColumn =
+            new IndentingWriter(rightWriter, rightWidth, spacer);
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param out non-null; stream to send final output to
+     * @param leftWidth &gt;= 1; width of the left column, in characters
+     * @param rightWidth &gt;= 1; width of the right column, in characters
+     * @param spacer non-null; spacer string to sit between the two columns
+     */
+    public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth,
+                           String spacer) {
+        this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
+    }
+
+    /**
+     * Gets the writer to use to write to the left column.
+     * 
+     * @return non-null; the left column writer
+     */
+    public Writer getLeft() {
+        return leftColumn;
+    }
+
+    /**
+     * Gets the writer to use to write to the right column.
+     * 
+     * @return non-null; the right column writer
+     */
+    public Writer getRight() {
+        return rightColumn;
+    }
+
+    /**
+     * Flushes the output. If there are more lines of pending output in one
+     * column, then the other column will get filled with blank lines.
+     */
+    public void flush() {
+        try {
+            appendNewlineIfNecessary(leftBuf, leftColumn);
+            appendNewlineIfNecessary(rightBuf, rightColumn);
+            outputFullLines();
+            flushLeft();
+            flushRight();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Outputs to the final destination as many full line pairs as
+     * there are in the pending output, removing those lines from
+     * their respective buffers. This method terminates when at
+     * least one of the two column buffers is empty.
+     */
+    private void outputFullLines() throws IOException {
+        for (;;) {
+            int leftLen = leftBuf.indexOf("\n");
+            if (leftLen < 0) {
+                return;
+            }
+
+            int rightLen = rightBuf.indexOf("\n");
+            if (rightLen < 0) {
+                return;
+            }
+
+            if (leftLen != 0) {
+                out.write(leftBuf.substring(0, leftLen));
+            }
+
+            if (rightLen != 0) {
+                writeSpaces(out, leftWidth - leftLen);
+                out.write(rightBuf.substring(0, rightLen));
+            }
+
+            out.write('\n');
+
+            leftBuf.delete(0, leftLen + 1);
+            rightBuf.delete(0, rightLen + 1);
+        }
+    }
+
+    /**
+     * Flushes the left column buffer, printing it and clearing the buffer.
+     * If the buffer is already empty, this does nothing.
+     */
+    private void flushLeft() throws IOException {
+        appendNewlineIfNecessary(leftBuf, leftColumn);
+
+        while (leftBuf.length() != 0) {
+            rightColumn.write('\n');
+            outputFullLines();
+        }
+    }
+
+    /**
+     * Flushes the right column buffer, printing it and clearing the buffer.
+     * If the buffer is already empty, this does nothing.
+     */
+    private void flushRight() throws IOException {
+        appendNewlineIfNecessary(rightBuf, rightColumn);
+
+        while (rightBuf.length() != 0) {
+            leftColumn.write('\n');
+            outputFullLines();
+        }
+    }
+
+    /**
+     * Appends a newline to the given buffer via the given writer, but
+     * only if it isn't empty and doesn't already end with one.
+     * 
+     * @param buf non-null; the buffer in question
+     * @param out non-null; the writer to use
+     */
+    private static void appendNewlineIfNecessary(StringBuffer buf,
+                                                 Writer out) 
+            throws IOException {
+        int len = buf.length();
+
+        if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
+            out.write('\n');
+        }
+    }
+
+    /**
+     * Writes the given number of spaces to the given writer.
+     * 
+     * @param out non-null; where to write
+     * @param amt &gt;= 0; the number of spaces to write
+     */
+    private static void writeSpaces(Writer out, int amt) throws IOException {
+        while (amt > 0) {
+            out.write(' ');
+            amt--;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/Warning.java b/dx/src/com/android/dx/util/Warning.java
new file mode 100644
index 0000000..3c23c7c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Warning.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception which is meant to indicate a non-fatal warning.
+ */
+public class Warning extends RuntimeException {
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     */
+    public Warning(String message) {
+        super(message);
+    }
+}
diff --git a/dx/src/com/android/dx/util/Writers.java b/dx/src/com/android/dx/util/Writers.java
new file mode 100644
index 0000000..f10e400
--- /dev/null
+++ b/dx/src/com/android/dx/util/Writers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Utilities for dealing with <code>Writer</code>s.
+ */
+public final class Writers {
+    /**
+     * This class is uninstantiable.
+     */
+    private Writers() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Makes a <code>PrintWriter</code> for the given <code>Writer</code>,
+     * returning the given writer if it already happens to be the right
+     * class.
+     * 
+     * @param writer non-null; writer to (possibly) wrap
+     * @return non-null; an appropriate instance
+     */
+    public static PrintWriter printWriterFor(Writer writer) {
+        if (writer instanceof PrintWriter) {
+            return (PrintWriter) writer;
+        }
+
+        return new PrintWriter(writer);
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_BitIntSet.java b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
new file mode 100644
index 0000000..e26d7a4
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntIterator;
+import com.android.dx.util.ListIntSet;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _BitIntSet extends TestCase {
+    public void test_basic() {
+        BitIntSet set = new BitIntSet(32);
+
+        assertEquals(0, set.elements());
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertEquals(3, set.elements());
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+    }
+
+    public void test_iterator() {
+        BitIntSet set = new BitIntSet(32);
+
+        set.add(0);
+        set.add(0);
+        set.add(1);
+        set.add(1);
+        set.add(31);
+        set.add(31);
+
+        IntIterator iter = set.iterator();
+
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 0);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 1);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 31);
+
+        assertFalse(iter.hasNext());
+
+        try {
+            iter.next();
+            fail();
+        } catch (NoSuchElementException ex) {
+            // exception excepted
+        }
+    }
+
+    public void test_remove() {
+        BitIntSet set = new BitIntSet(32);
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+
+        set.remove(0);
+
+        assertFalse(set.has(0));
+
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+    }
+
+    /**
+     * Tests the auto-expansion of the set
+     */
+    public void test_expand() {
+        BitIntSet set = new BitIntSet(32);
+        int[] values = {0, 1, 31, 32, 128};
+
+        for (int i = 0; i < values.length; i++) {
+            set.add(values[i]);
+        }
+
+        IntIterator iter = set.iterator();
+
+        for (int i = 0; i < values.length; i++) {
+            assertTrue(iter.hasNext());
+            assertEquals(values[i], iter.next());
+        }
+        assertFalse(iter.hasNext());
+    }
+
+    public void test_merge() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(32);
+        int[] valuesB = {0, 5, 6, 8, 31};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_mergeWithListIntSet() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 8, 31};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_mergeAndExpand() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(32);
+        int[] valuesB = {0, 5, 6, 32, 127};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_toString() {
+        BitIntSet set = new BitIntSet(32);
+
+        assertEquals(set.toString(), "{}");
+
+        set.add(1);
+
+        assertEquals(set.toString(), "{1}");
+
+        set.add(2);
+
+        assertEquals(set.toString(), "{1, 2}");
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_Bits.java b/dx/src/com/android/dx/util/_tests/_Bits.java
new file mode 100644
index 0000000..e529b50
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_Bits.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.Bits;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class <code>com.android.dx.util.Bits</code>.
+ */
+public class _Bits
+        extends TestCase {
+    public void test_makeBitSet() {
+        assertEquals(label(0), 0, Bits.makeBitSet(0).length);
+
+        for (int i = 1; i <= 32; i++) {
+            assertEquals(label(i), 1, Bits.makeBitSet(i).length);
+        }
+
+        for (int i = 33; i <= 64; i++) {
+            assertEquals(label(i), 2, Bits.makeBitSet(i).length);
+        }
+
+        for (int i = 65; i < 4000; i += 101) {
+            int expect = i >> 5;
+            if ((expect * 32) < i) {
+                expect++;
+            }
+            assertEquals(label(i), expect, Bits.makeBitSet(i).length);
+        }
+    }
+
+    public void test_getMax() {
+        for (int i = 0; i < 4000; i += 59) {
+            int expect = i >> 5;
+            if ((expect * 32) < i) {
+                expect++;
+            }
+            assertEquals(label(i), expect * 32,
+                         Bits.getMax(new int[expect]));
+        }
+    }
+
+    public void test1_get() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            assertFalse(label(i), Bits.get(bits, i));
+        }
+    }
+
+    public void test2_get() {
+        int[] bits = Bits.makeBitSet(100);
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            assertTrue(label(i), Bits.get(bits, i));
+        }
+    }
+
+    public void test3_get() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            Bits.set(bits, i, (i % 5) == 0);
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 5) == 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test1_set1() {
+        int[] bits = Bits.makeBitSet(50);
+        bits[1] = -1;
+
+        Bits.set(bits, 0, true);
+        Bits.set(bits, 3, true);
+        Bits.set(bits, 6, true);
+        Bits.set(bits, 3, false);
+        Bits.set(bits, 35, false);
+        Bits.set(bits, 38, false);
+        Bits.set(bits, 42, false);
+        Bits.set(bits, 38, true);
+
+        assertEquals(label(1), 0x41, bits[0]);
+        assertEquals(label(2), 0xfffffbf7, bits[1]);
+    }
+
+    public void test2_set1() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 3) == 0) {
+                Bits.set(bits, i, true);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 5) == 0) {
+                Bits.set(bits, i, false);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 7) == 0) {
+                Bits.set(bits, i, true);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = ((i % 7) == 0) ||
+                (((i % 3) == 0) && ((i % 5) != 0));
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test_set2() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 11) == 0) {
+                Bits.set(bits, i);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 11) == 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test_clear() {
+        int[] bits = Bits.makeBitSet(100);
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 5) == 0) {
+                Bits.clear(bits, i);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 5) != 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test1_isEmpty() {
+        for (int i = 0; i < 10; i++) {
+            assertTrue(label(i), Bits.isEmpty(new int[i]));
+        }
+    }
+
+    public void test2_isEmpty() {
+        for (int i = 1; i < 1000; i += 11) {
+            int[] bits = Bits.makeBitSet(i);
+            for (int j = i % 11; j >= 0; j--) {
+                int x = i - 1 - (j * 13);
+                if (x >= 0) {
+                    Bits.set(bits, x);
+                }
+            }
+            assertFalse(label(i), Bits.isEmpty(bits));
+        }
+    }
+
+    public void test1_bitCount() {
+        for (int i = 0; i < 10; i++) {
+            assertEquals(label(i), 0, Bits.bitCount(new int[i]));
+        }
+    }
+
+    public void test2_bitCount() {
+        for (int i = 1; i < 1000; i += 13) {
+            int[] bits = Bits.makeBitSet(i);
+            int count = 0;
+            for (int j = 0; j < i; j += 20) {
+                Bits.set(bits, j);
+                count++;
+            }
+            for (int j = 7; j < i; j += 11) {
+                if (!Bits.get(bits, j)) {
+                    Bits.set(bits, j);
+                    count++;
+                }
+            }
+            for (int j = 3; j < i; j += 17) {
+                if (!Bits.get(bits, j)) {
+                    Bits.set(bits, j);
+                    count++;
+                }
+            }
+            assertEquals(label(i), count, Bits.bitCount(bits));
+        }
+    }
+
+    public void test1_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i += 11) {
+            assertFalse(label(i), Bits.anyInRange(bits, 0, i));
+        }
+    }
+
+    public void test2_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i += 11) {
+            assertFalse(label(i), Bits.anyInRange(bits, i, 100));
+        }
+    }
+
+    public void test3_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 50; i += 7) {
+            assertFalse(label(i), Bits.anyInRange(bits, i, 100 - i));
+        }
+    }
+
+    public void test4_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 1; i < 100; i += 11) {
+            assertTrue(label(i), Bits.anyInRange(bits, 0, i));
+        }
+    }
+
+    public void test5_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 1; i < 100; i += 11) {
+            assertTrue(label(i), Bits.anyInRange(bits, i, 100));
+        }
+    }
+
+    public void test6_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 50; i += 7) {
+            assertTrue(label(i), Bits.anyInRange(bits, i, 100 - i));
+        }
+    }
+
+    public void test1_findFirst1() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i++) {
+            assertEquals(label(i), -1, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test2_findFirst1() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            assertEquals(label(i), i, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test3_findFirst1() {
+        int[] bits = new int[100];
+
+        for (int i = 25; i < 80; i++) {
+            for (int j = 0; j < bits.length; j++) {
+                bits[j] = 0;
+            }
+
+            Bits.set(bits, i - 5);
+            Bits.set(bits, i + 5);
+            Bits.set(bits, i + 10);
+            Bits.set(bits, i + 20);
+            assertEquals(label(i), i + 5, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test1_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), -1, Bits.findFirst(0, i));
+        }
+    }
+
+    public void test2_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), i, Bits.findFirst(-1, i));
+        }
+    }
+
+    public void test3_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), -1, Bits.findFirst((1 << i) >>> 1, i));
+        }
+    }
+
+    public void test4_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), i, Bits.findFirst(1 << i, i));
+        }
+    }
+
+    public void test5_findFirst2() {
+        for (int i = 0; i < 31; i++) {
+            assertEquals(label(i), i + 1, Bits.findFirst(1 << (i + 1), i));
+        }
+    }
+
+    public void test6_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            int value = (1 << i);
+            value |= (value >>> 1);
+            assertEquals(label(i), i, Bits.findFirst(value, i));
+        }
+    }
+
+    private static String label(int n) {
+        return "(" + n + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_IntList.java b/dx/src/com/android/dx/util/_tests/_IntList.java
new file mode 100644
index 0000000..241e8be
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_IntList.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.IntList;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class <code>com.android.dx.util.IntList</code>.
+ */
+public class _IntList
+    extends TestCase {
+    // TODO: Add tests for the rest of the methods.
+
+    public void test_contains() {
+        for (int sz = 0; sz < 100; sz++) {
+            IntList list = new IntList(sz);
+            for (int i = 0; i < sz; i++) {
+                list.add(i * 2);
+            }
+            for (int i = (sz * 2) - 1; i >= 0; i--) {
+                boolean contains = list.contains(i);
+                if ((i & 1) == 0) {
+                    assertTrue(label(sz, i), contains);
+                } else {
+                    assertFalse(label(sz, i), contains);
+                }
+            }
+            assertFalse(label(sz, -1), list.contains(-1));
+            assertFalse(label(sz, sz * 2), list.contains(sz * 2));
+        }
+    }
+
+    public void test_addSorted() {
+        IntList list = new IntList(2);
+
+        list.add(9);
+        list.add(12);
+
+        assertTrue(list.contains(9));
+        assertTrue(list.contains(12));
+    }
+
+    public void test_addUnsorted() {
+        IntList list = new IntList(2);
+
+        list.add(12);
+        list.add(9);
+
+        assertTrue(list.contains(12));
+        assertTrue(list.contains(9));
+    }
+
+    private static String label(int n, int m) {
+        return "(" + n + "/" + m + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_ListIntSet.java b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
new file mode 100644
index 0000000..aed79b0
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+import com.android.dx.util.IntIterator;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _ListIntSet extends TestCase {
+    public void test_basic() {
+        ListIntSet set = new ListIntSet();
+
+        assertEquals(0, set.elements());
+
+        set.add(31);
+        set.add(0);
+        set.add(1);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertEquals(3, set.elements());
+        
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+    }
+
+    public void test_iterator() {
+        ListIntSet set = new ListIntSet();
+
+        set.add(0);
+        set.add(0);
+        set.add(1);
+        set.add(1);
+        set.add(31);
+        set.add(31);
+
+        IntIterator iter = set.iterator();
+
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 0);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 1);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 31);
+
+        assertFalse(iter.hasNext());
+
+        try {
+            iter.next();
+            fail();
+        } catch (NoSuchElementException ex) {
+            // exception excepted
+        }
+    }
+
+    public void test_empty() {
+        ListIntSet set = new ListIntSet();
+
+        IntIterator iter = set.iterator();
+
+        assertFalse(iter.hasNext());        
+    }
+
+    public void test_remove() {
+        ListIntSet set = new ListIntSet();
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+
+        set.remove(0);
+
+        assertFalse(set.has(0));
+
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+    }
+
+    public void test_mergeA() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 32, 127, 128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_mergeB() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31, 129, 130};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 32, 127,128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_mergeWithBitIntSet() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31, 129, 130};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(129);
+        int[] valuesB = {0, 5, 6, 32, 127,128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_toString() {
+        ListIntSet set = new ListIntSet();
+
+        assertEquals(set.toString(), "{}");
+
+        set.add(1);
+
+        assertEquals(set.toString(), "{1}");
+
+        set.add(2);
+
+        assertEquals(set.toString(), "{1, 2}");
+    }
+
+}
diff --git a/dx/src/com/android/dx/util/package.html b/dx/src/com/android/dx/util/package.html
new file mode 100644
index 0000000..8e81e94
--- /dev/null
+++ b/dx/src/com/android/dx/util/package.html
@@ -0,0 +1,3 @@
+<body>
+<p>Utility classes for class file access/manipulation.</p>
+</body>
diff --git a/dx/src/junit/extensions/ActiveTestSuite.java b/dx/src/junit/extensions/ActiveTestSuite.java
new file mode 100644
index 0000000..1a3f163
--- /dev/null
+++ b/dx/src/junit/extensions/ActiveTestSuite.java
@@ -0,0 +1,64 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestSuite for active Tests. It runs each
+ * test in a separate thread and waits until all
+ * threads have terminated.
+ * -- Aarhus Radisson Scandinavian Center 11th floor
+ */ 
+public class ActiveTestSuite extends TestSuite {
+	private volatile int fActiveTestDeathCount;
+
+	public ActiveTestSuite() {
+	}
+		
+	public ActiveTestSuite(Class theClass) {
+		super(theClass);
+	}
+	
+	public ActiveTestSuite(String name) {
+		super (name);
+	}
+	
+	public ActiveTestSuite(Class theClass, String name) {
+		super(theClass, name);
+	}
+	
+	public void run(TestResult result) {
+		fActiveTestDeathCount= 0;
+		super.run(result);
+		waitUntilFinished();
+	}
+	
+	public void runTest(final Test test, final TestResult result) {
+		Thread t= new Thread() {
+			public void run() {
+				try {
+					// inlined due to limitation in VA/Java 
+					//ActiveTestSuite.super.runTest(test, result);
+					test.run(result);
+				} finally {
+					ActiveTestSuite.this.runFinished(test);
+				}
+			}
+		};
+		t.start();
+	}
+
+	synchronized void waitUntilFinished() {
+		while (fActiveTestDeathCount < testCount()) {
+			try {
+				wait();
+			} catch (InterruptedException e) {
+				return; // ignore
+			}
+		}
+	}
+	
+	synchronized public void runFinished(Test test) {
+		fActiveTestDeathCount++;
+		notifyAll();
+	}
+}
diff --git a/dx/src/junit/extensions/ExceptionTestCase.java b/dx/src/junit/extensions/ExceptionTestCase.java
new file mode 100644
index 0000000..fae5746
--- /dev/null
+++ b/dx/src/junit/extensions/ExceptionTestCase.java
@@ -0,0 +1,46 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestCase that expects an Exception of class fExpected to be thrown.
+ * The other way to check that an expected exception is thrown is:
+ * <pre>
+ * try {
+ *   shouldThrow();
+ * }
+ * catch (SpecialException e) {
+ *   return;
+ * }
+ * fail("Expected SpecialException");
+ * </pre>
+ *
+ * To use ExceptionTestCase, create a TestCase like:
+ * <pre>
+ * new ExceptionTestCase("testShouldThrow", SpecialException.class);
+ * </pre>
+ */
+public class ExceptionTestCase extends TestCase {
+	Class<?> fExpected;
+
+	public ExceptionTestCase(String name, Class exception) {
+		super(name);
+		fExpected= exception;
+	}
+	/**
+	 * Execute the test method expecting that an Exception of
+	 * class fExpected or one of its subclasses will be thrown
+	 */
+	protected void runTest() throws Throwable {
+		try {
+			super.runTest();
+		}
+		catch (Exception e) {
+			if (fExpected.isAssignableFrom(e.getClass()))
+				return;
+			else
+				throw e;
+		}
+		fail("Expected exception " + fExpected);
+	}
+}
diff --git a/dx/src/junit/extensions/RepeatedTest.java b/dx/src/junit/extensions/RepeatedTest.java
new file mode 100644
index 0000000..ebce775
--- /dev/null
+++ b/dx/src/junit/extensions/RepeatedTest.java
@@ -0,0 +1,31 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator that runs a test repeatedly.
+ *
+ */
+public class RepeatedTest extends  TestDecorator {
+	private int fTimesRepeat;
+
+	public RepeatedTest(Test test, int repeat) {
+		super(test);
+		if (repeat < 0)
+			throw new IllegalArgumentException("Repetition count must be > 0");
+		fTimesRepeat= repeat;
+	}
+	public int countTestCases() {
+		return super.countTestCases()*fTimesRepeat;
+	}
+	public void run(TestResult result) {
+		for (int i= 0; i < fTimesRepeat; i++) {
+			if (result.shouldStop())
+				break;
+			super.run(result);
+		}
+	}
+	public String toString() {
+		return super.toString()+"(repeated)";
+	}
+}
diff --git a/dx/src/junit/extensions/TestDecorator.java b/dx/src/junit/extensions/TestDecorator.java
new file mode 100644
index 0000000..a8e9e7d
--- /dev/null
+++ b/dx/src/junit/extensions/TestDecorator.java
@@ -0,0 +1,38 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator for Tests. Use TestDecorator as the base class
+ * for defining new test decorators. Test decorator subclasses
+ * can be introduced to add behaviour before or after a test
+ * is run.
+ *
+ */
+public class TestDecorator extends Assert implements Test {
+	protected Test fTest;
+
+	public TestDecorator(Test test) {
+		fTest= test;
+	}
+	/**
+	 * The basic run behaviour.
+	 */
+	public void basicRun(TestResult result) {
+		fTest.run(result);
+	}
+	public int countTestCases() {
+		return fTest.countTestCases();
+	}
+	public void run(TestResult result) {
+		basicRun(result);
+	}
+
+	public String toString() {
+		return fTest.toString();
+	}
+
+	public Test getTest() {
+		return fTest;
+	}
+}
diff --git a/dx/src/junit/extensions/TestSetup.java b/dx/src/junit/extensions/TestSetup.java
new file mode 100644
index 0000000..f1c25fa
--- /dev/null
+++ b/dx/src/junit/extensions/TestSetup.java
@@ -0,0 +1,37 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator to set up and tear down additional fixture state.
+ * Subclass TestSetup and insert it into your tests when you want
+ * to set up additional state once before the tests are run.
+ */
+public class TestSetup extends TestDecorator {
+
+	public TestSetup(Test test) {
+		super(test);
+	}
+	public void run(final TestResult result) {
+		Protectable p= new Protectable() {
+			public void protect() throws Exception {
+				setUp();
+				basicRun(result);
+				tearDown();
+			}
+		};
+		result.runProtected(this, p);
+	}
+	/**
+	 * Sets up the fixture. Override to set up additional fixture
+	 * state.
+	 */
+	protected void setUp() throws Exception {
+	}
+	/**
+	 * Tears down the fixture. Override to tear down the additional
+	 * fixture state.
+	 */
+	protected void tearDown() throws Exception {
+	}
+}
diff --git a/dx/src/junit/framework/Assert.java b/dx/src/junit/framework/Assert.java
new file mode 100644
index 0000000..eb5d960
--- /dev/null
+++ b/dx/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods.  Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+	/**
+	 * Protect constructor since it is a static only class
+	 */
+	protected Assert() {
+	}
+
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertTrue(String message, boolean condition) {
+		if (!condition)
+			fail(message);
+	}
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertTrue(boolean condition) {
+		assertTrue(null, condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertFalse(String message, boolean condition) {
+		assertTrue(message, !condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertFalse(boolean condition) {
+		assertFalse(null, condition);
+	}
+	/**
+	 * Fails a test with the given message.
+	 */
+	static public void fail(String message) {
+		throw new AssertionFailedError(message);
+	}
+	/**
+	 * Fails a test with no message.
+	 */
+	static public void fail() {
+		fail(null);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, Object expected, Object actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		failNotEquals(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown.
+	 */
+	static public void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String message, String expected, String actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		throw new ComparisonFailure(message, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta.  If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, double expected, double actual, double delta) {
+		// handle infinity specially since subtracting to infinite values gives NaN and the
+		// the following test fails
+		if (Double.isInfinite(expected)) {
+			if (!(expected == actual))
+				failNotEquals(message, new Double(expected), new Double(actual));
+		} else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+			failNotEquals(message, new Double(expected), new Double(actual));
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(double expected, double actual, double delta) {
+	    assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, float expected, float actual, float delta) {
+ 		// handle infinity specially since subtracting to infinite values gives NaN and the
+		// the following test fails
+		if (Float.isInfinite(expected)) {
+			if (!(expected == actual))
+				failNotEquals(message, new Float(expected), new Float(actual));
+		} else if (!(Math.abs(expected-actual) <= delta))
+      		failNotEquals(message, new Float(expected), new Float(actual));
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(float expected, float actual, float delta) {
+		assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two longs are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, long expected, long actual) {
+	    assertEquals(message, new Long(expected), new Long(actual));
+	}
+	/**
+	 * Asserts that two longs are equal.
+	 */
+	static public void assertEquals(long expected, long actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two booleans are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, boolean expected, boolean actual) {
+    		assertEquals(message, new Boolean(expected), new Boolean(actual));
+  	}
+	/**
+	 * Asserts that two booleans are equal.
+ 	 */
+	static public void assertEquals(boolean expected, boolean actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two bytes are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, byte expected, byte actual) {
+		assertEquals(message, new Byte(expected), new Byte(actual));
+	}
+	/**
+   	 * Asserts that two bytes are equal.
+	 */
+	static public void assertEquals(byte expected, byte actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two chars are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, char expected, char actual) {
+    		assertEquals(message, new Character(expected), new Character(actual));
+  	}
+	/**
+	 * Asserts that two chars are equal.
+	 */
+  	static public void assertEquals(char expected, char actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two shorts are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, short expected, short actual) {
+    		assertEquals(message, new Short(expected), new Short(actual));
+	}
+  	/**
+	 * Asserts that two shorts are equal.
+	 */
+	static public void assertEquals(short expected, short actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two ints are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, int expected, int actual) {
+		assertEquals(message, new Integer(expected), new Integer(actual));
+  	}
+  	/**
+   	 * Asserts that two ints are equal.
+	 */
+  	static public void assertEquals(int expected, int actual) {
+  		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that an object isn't null.
+	 */
+	static public void assertNotNull(Object object) {
+		assertNotNull(null, object);
+	}
+	/**
+	 * Asserts that an object isn't null. If it is
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNotNull(String message, Object object) {
+		assertTrue(message, object != null);
+	}
+	/**
+	 * Asserts that an object is null.
+	 */
+	static public void assertNull(Object object) {
+		assertNull(null, object);
+	}
+	/**
+	 * Asserts that an object is null.  If it is not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNull(String message, Object object) {
+		assertTrue(message, object == null);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			return;
+		failNotSame(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertSame(Object expected, Object actual) {
+	    assertSame(null, expected, actual);
+	}
+ 	/**
+ 	 * Asserts that two objects refer to the same object. If they are not
+ 	 * an AssertionFailedError is thrown with the given message.
+ 	 */
+	static public void assertNotSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			failSame(message);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertNotSame(Object expected, Object actual) {
+		assertNotSame(null, expected, actual);
+	}
+
+	static private void failSame(String message) {
+		String formatted= "";
+ 		if (message != null)
+ 			formatted= message+" ";
+ 		fail(formatted+"expected not same");
+	}
+
+	static private void failNotSame(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+	}
+
+	static private void failNotEquals(String message, Object expected, Object actual) {
+		fail(format(message, expected, actual));
+	}
+
+	static String format(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/dx/src/junit/framework/AssertionFailedError.java b/dx/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..9aee001
--- /dev/null
+++ b/dx/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+	public AssertionFailedError () {
+	}
+	public AssertionFailedError (String message) {
+		super (message);
+	}
+}
diff --git a/dx/src/junit/framework/ComparisonFailure.java b/dx/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..1bfe591
--- /dev/null
+++ b/dx/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ * 
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+	private String fExpected;
+	private String fActual;
+
+	/**
+	 * Constructs a comparison failure.
+	 * @param message the identifying message or null
+	 * @param expected the expected string value
+	 * @param actual the actual string value
+	 */
+	public ComparisonFailure (String message, String expected, String actual) {
+		super (message);
+		fExpected= expected;
+		fActual= actual;
+	}
+	
+	/**
+	 * Returns "..." in place of common prefix and "..." in
+	 * place of common suffix between expected and actual.
+	 * 
+	 * @see java.lang.Throwable#getMessage()
+	 */
+	public String getMessage() {
+		if (fExpected == null || fActual == null)
+			return Assert.format(super.getMessage(), fExpected, fActual);
+			
+		int end= Math.min(fExpected.length(), fActual.length());
+		
+		int i= 0;
+		for(; i < end; i++) {
+			if (fExpected.charAt(i) != fActual.charAt(i))
+				break;
+		}
+		int j= fExpected.length()-1;
+		int k= fActual.length()-1;
+		for (; k >= i && j >= i; k--,j--) {
+			if (fExpected.charAt(j) != fActual.charAt(k))
+				break;
+		}
+		String actual, expected;
+		
+		// equal strings
+		if (j < i && k < i) {
+			expected= fExpected;
+			actual= fActual;
+		} else {
+			expected= fExpected.substring(i, j+1);
+			actual= fActual.substring(i, k+1);
+			if (i <= end && i > 0) {
+				expected= "..."+expected;
+				actual= "..."+actual;
+			}
+			
+			if (j < fExpected.length()-1)
+				expected= expected+"...";
+			if (k < fActual.length()-1)
+				actual= actual+"...";
+		}	
+		return Assert.format(super.getMessage(), expected, actual);
+	}
+}
diff --git a/dx/src/junit/framework/Protectable.java b/dx/src/junit/framework/Protectable.java
new file mode 100644
index 0000000..8243555
--- /dev/null
+++ b/dx/src/junit/framework/Protectable.java
@@ -0,0 +1,14 @@
+package junit.framework;
+
+/**
+ * A <em>Protectable</em> can be run and can throw a Throwable.
+ *
+ * @see TestResult
+ */
+public interface Protectable {
+
+	/**
+	 * Run the the following method protected.
+	 */
+	public abstract void protect() throws Throwable;
+}
diff --git a/dx/src/junit/framework/Test.java b/dx/src/junit/framework/Test.java
new file mode 100644
index 0000000..1c6d57b
--- /dev/null
+++ b/dx/src/junit/framework/Test.java
@@ -0,0 +1,17 @@
+package junit.framework;
+
+/**
+ * A <em>Test</em> can be run and collect its results.
+ *
+ * @see TestResult
+ */
+public interface Test {
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public abstract int countTestCases();
+	/**
+	 * Runs a test and collects its result in a TestResult instance.
+	 */
+	public abstract void run(TestResult result);
+}
diff --git a/dx/src/junit/framework/TestCase.java b/dx/src/junit/framework/TestCase.java
new file mode 100644
index 0000000..8988c45
--- /dev/null
+++ b/dx/src/junit/framework/TestCase.java
@@ -0,0 +1,197 @@
+package junit.framework;
+
+import java.lang.reflect.*;
+
+/**
+ * A test case defines the fixture to run multiple tests. To define a test case<br>
+ * 1) implement a subclass of TestCase<br>
+ * 2) define instance variables that store the state of the fixture<br>
+ * 3) initialize the fixture state by overriding <code>setUp</code><br>
+ * 4) clean-up after a test by overriding <code>tearDown</code>.<br>
+ * Each test runs in its own fixture so there
+ * can be no side effects among test runs.
+ * Here is an example:
+ * <pre>
+ * public class MathTest extends TestCase {
+ *     protected double fValue1;
+ *     protected double fValue2;
+ *
+ *    protected void setUp() {
+ *         fValue1= 2.0;
+ *         fValue2= 3.0;
+ *     }
+ * }
+ * </pre>
+ *
+ * For each test implement a method which interacts
+ * with the fixture. Verify the expected results with assertions specified
+ * by calling <code>assertTrue</code> with a boolean.
+ * <pre>
+ *    public void testAdd() {
+ *        double result= fValue1 + fValue2;
+ *        assertTrue(result == 5.0);
+ *    }
+ * </pre>
+ * Once the methods are defined you can run them. The framework supports
+ * both a static type safe and more dynamic way to run a test.
+ * In the static way you override the runTest method and define the method to
+ * be invoked. A convenient way to do so is with an anonymous inner class.
+ * <pre>
+ * TestCase test= new MathTest("add") {
+ *        public void runTest() {
+ *            testAdd();
+ *        }
+ * };
+ * test.run();
+ * </pre>
+ * The dynamic way uses reflection to implement <code>runTest</code>. It dynamically finds
+ * and invokes a method.
+ * In this case the name of the test case has to correspond to the test method
+ * to be run.
+ * <pre>
+ * TestCase= new MathTest("testAdd");
+ * test.run();
+ * </pre>
+ * The tests to be run can be collected into a TestSuite. JUnit provides
+ * different <i>test runners</i> which can run a test suite and collect the results.
+ * A test runner either expects a static method <code>suite</code> as the entry
+ * point to get a test to run or it will extract the suite automatically.
+ * <pre>
+ * public static Test suite() {
+ *      suite.addTest(new MathTest("testAdd"));
+ *      suite.addTest(new MathTest("testDivideByZero"));
+ *      return suite;
+ *  }
+ * </pre>
+ * @see TestResult
+ * @see TestSuite
+ */
+
+public abstract class TestCase extends Assert implements Test {
+	/**
+	 * the name of the test case
+	 */
+	private String fName;
+
+	/**
+	 * No-arg constructor to enable serialization. This method
+	 * is not intended to be used by mere mortals without calling setName().
+	 */
+	public TestCase() {
+		fName= null;
+	}
+	/**
+	 * Constructs a test case with the given name.
+	 */
+	public TestCase(String name) {
+		fName= name;
+	}
+	/**
+	 * Counts the number of test cases executed by run(TestResult result).
+	 */
+	public int countTestCases() {
+		return 1;
+	}
+	/**
+	 * Creates a default TestResult object
+	 *
+	 * @see TestResult
+	 */
+	protected TestResult createResult() {
+	    return new TestResult();
+	}
+	/**
+	 * A convenience method to run this test, collecting the results with a
+	 * default TestResult object.
+	 *
+	 * @see TestResult
+	 */
+	public TestResult run() {
+		TestResult result= createResult();
+		run(result);
+		return result;
+	}
+	/**
+	 * Runs the test case and collects the results in TestResult.
+	 */
+	public void run(TestResult result) {
+		result.run(this);
+	}
+	/**
+	 * Runs the bare test sequence.
+	 * @exception Throwable if any exception is thrown
+	 */
+	public void runBare() throws Throwable {
+		setUp();
+		try {
+			runTest();
+		}
+		finally {
+			tearDown();
+		}
+	}
+	/**
+	 * Override to run the test and assert its state.
+	 * @exception Throwable if any exception is thrown
+	 */
+	protected void runTest() throws Throwable {
+		assertNotNull(fName);
+		Method runMethod= null;
+		try {
+			// use getMethod to get all public inherited
+			// methods. getDeclaredMethods returns all
+			// methods of this class but excludes the
+			// inherited ones.
+			runMethod= getClass().getMethod(fName, (Class[]) null);
+		} catch (NoSuchMethodException e) {
+			fail("Method \""+fName+"\" not found");
+		}
+		if (!Modifier.isPublic(runMethod.getModifiers())) {
+			fail("Method \""+fName+"\" should be public");
+		}
+
+		try {
+			runMethod.invoke(this, (Object[]) new Class[0]);
+		}
+		catch (InvocationTargetException e) {
+			e.fillInStackTrace();
+			throw e.getTargetException();
+		}
+		catch (IllegalAccessException e) {
+			e.fillInStackTrace();
+			throw e;
+		}
+	}
+	/**
+	 * Sets up the fixture, for example, open a network connection.
+	 * This method is called before a test is executed.
+	 */
+	protected void setUp() throws Exception {
+	}
+	/**
+	 * Tears down the fixture, for example, close a network connection.
+	 * This method is called after a test is executed.
+	 */
+	protected void tearDown() throws Exception {
+	}
+	/**
+	 * Returns a string representation of the test case
+	 */
+	public String toString() {
+	    return getName() + "(" + getClass().getName() + ")";
+	}
+	/**
+	 * Gets the name of a TestCase
+	 * @return returns a String
+	 */
+	public String getName() {
+		return fName;
+	}
+	/**
+	 * Sets the name of a TestCase
+	 * @param name The name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+}
diff --git a/dx/src/junit/framework/TestFailure.java b/dx/src/junit/framework/TestFailure.java
new file mode 100644
index 0000000..664747d
--- /dev/null
+++ b/dx/src/junit/framework/TestFailure.java
@@ -0,0 +1,57 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * A <code>TestFailure</code> collects a failed test together with
+ * the caught exception.
+ * @see TestResult
+ */
+public class TestFailure extends Object {
+	protected Test fFailedTest;
+	protected Throwable fThrownException;
+	
+
+	/**
+	 * Constructs a TestFailure with the given test and exception.
+	 */
+	public TestFailure(Test failedTest, Throwable thrownException) {
+		fFailedTest= failedTest;
+		fThrownException= thrownException;
+	}
+	/**
+	 * Gets the failed test.
+	 */
+	public Test failedTest() {
+	    return fFailedTest;
+	}
+	/**
+	 * Gets the thrown exception.
+	 */
+	public Throwable thrownException() {
+	    return fThrownException;
+	}
+	/**
+	 * Returns a short description of the failure.
+	 */
+	public String toString() {
+	    StringBuffer buffer= new StringBuffer();
+	    buffer.append(fFailedTest+": "+fThrownException.getMessage());
+	    return buffer.toString();
+	}
+	public String trace() {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		thrownException().printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		return buffer.toString();
+	}
+	public String exceptionMessage() {
+		return thrownException().getMessage();
+	}
+	public boolean isFailure() {
+		return thrownException() instanceof AssertionFailedError;
+	}
+}
diff --git a/dx/src/junit/framework/TestListener.java b/dx/src/junit/framework/TestListener.java
new file mode 100644
index 0000000..7558187
--- /dev/null
+++ b/dx/src/junit/framework/TestListener.java
@@ -0,0 +1,23 @@
+package junit.framework;
+
+/**
+ * A Listener for test progress
+ */
+public interface TestListener {
+	/**
+ 	 * An error occurred.
+ 	 */
+	public void addError(Test test, Throwable t);
+	/**
+ 	 * A failure occurred.
+ 	 */
+ 	public void addFailure(Test test, AssertionFailedError t);  
+	/**
+	 * A test ended.
+	 */
+ 	public void endTest(Test test); 
+	/**
+	 * A test started.
+	 */
+	public void startTest(Test test);
+}
diff --git a/dx/src/junit/framework/TestResult.java b/dx/src/junit/framework/TestResult.java
new file mode 100644
index 0000000..4b1a7e2
--- /dev/null
+++ b/dx/src/junit/framework/TestResult.java
@@ -0,0 +1,166 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * A <code>TestResult</code> collects the results of executing
+ * a test case. It is an instance of the Collecting Parameter pattern.
+ * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
+ * A failure is anticipated and checked for with assertions. Errors are
+ * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
+ *
+ * @see Test
+ */
+public class TestResult extends Object {
+	protected Vector<TestFailure> fFailures;
+	protected Vector<TestFailure> fErrors;
+	protected Vector<TestListener> fListeners;
+	protected int fRunTests;
+	private boolean fStop;
+	
+	public TestResult() {
+		fFailures= new Vector<TestFailure>();
+		fErrors= new Vector<TestFailure>();
+		fListeners= new Vector<TestListener>();
+		fRunTests= 0;
+		fStop= false;
+	}
+	/**
+	 * Adds an error to the list of errors. The passed in exception
+	 * caused the error.
+	 */
+	public synchronized void addError(Test test, Throwable t) {
+		fErrors.addElement(new TestFailure(test, t));
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).addError(test, t);
+		}
+	}
+	/**
+	 * Adds a failure to the list of failures. The passed in exception
+	 * caused the failure.
+	 */
+	public synchronized void addFailure(Test test, AssertionFailedError t) {
+		fFailures.addElement(new TestFailure(test, t));
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).addFailure(test, t);
+		}
+	}
+	/**
+	 * Registers a TestListener
+	 */
+	public synchronized void addListener(TestListener listener) {
+		fListeners.addElement(listener);
+	}
+	/**
+	 * Unregisters a TestListener
+	 */
+	public synchronized void removeListener(TestListener listener) {
+		fListeners.removeElement(listener);
+	}
+	/**
+	 * Returns a copy of the listeners.
+	 */
+	private synchronized Vector cloneListeners() {
+		return (Vector)fListeners.clone();
+	}
+	/**
+	 * Informs the result that a test was completed.
+	 */
+	public void endTest(Test test) {
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).endTest(test);
+		}
+	}
+	/**
+	 * Gets the number of detected errors.
+	 */
+	public synchronized int errorCount() {
+		return fErrors.size();
+	}
+	/**
+	 * Returns an Enumeration for the errors
+	 */
+	public synchronized Enumeration errors() {
+		return fErrors.elements();
+	}
+	/**
+	 * Gets the number of detected failures.
+	 */
+	public synchronized int failureCount() {
+		return fFailures.size();
+	}
+	/**
+	 * Returns an Enumeration for the failures
+	 */
+	public synchronized Enumeration failures() {
+		return fFailures.elements();
+	}
+	/**
+	 * Runs a TestCase.
+	 */
+	protected void run(final TestCase test) {
+		startTest(test);
+		Protectable p= new Protectable() {
+			public void protect() throws Throwable {
+				test.runBare();
+			}
+		};
+		runProtected(test, p);
+
+		endTest(test);
+	}
+	/**
+	 * Gets the number of run tests.
+	 */
+	public synchronized int runCount() {
+		return fRunTests;
+	}
+	/**
+	 * Runs a TestCase.
+	 */
+	public void runProtected(final Test test, Protectable p) {
+		try {
+			p.protect();
+		} 
+		catch (AssertionFailedError e) {
+			addFailure(test, e);
+		}
+		catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+			throw e;
+		}
+		catch (Throwable e) {
+			addError(test, e);
+		}
+	}
+	/**
+	 * Checks whether the test run should stop
+	 */
+	public synchronized boolean shouldStop() {
+		return fStop;
+	}
+	/**
+	 * Informs the result that a test will be started.
+	 */
+	public void startTest(Test test) {
+		final int count= test.countTestCases();
+		synchronized(this) {
+			fRunTests+= count;
+		}
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).startTest(test);
+		}
+	}
+	/**
+	 * Marks that the test run should stop.
+	 */
+	public synchronized void stop() {
+		fStop= true;
+	}
+	/**
+	 * Returns whether the entire test was successful or not.
+	 */
+	public synchronized boolean wasSuccessful() {
+		return failureCount() == 0 && errorCount() == 0;
+	}
+}
diff --git a/dx/src/junit/framework/TestSuite.java b/dx/src/junit/framework/TestSuite.java
new file mode 100644
index 0000000..2987ad1
--- /dev/null
+++ b/dx/src/junit/framework/TestSuite.java
@@ -0,0 +1,265 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.PrintWriter;import java.io.StringWriter;import java.lang.reflect.*;
+import java.lang.reflect.Constructor;
+
+/**
+ * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * It runs a collection of test cases. Here is an example using
+ * the dynamic test definition.
+ * <pre>
+ * TestSuite suite= new TestSuite();
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * </pre>
+ * Alternatively, a TestSuite can extract the tests to be run automatically.
+ * To do so you pass the class of your TestCase class to the
+ * TestSuite constructor.
+ * <pre>
+ * TestSuite suite= new TestSuite(MathTest.class);
+ * </pre>
+ * This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.
+ *
+ * @see Test
+ */
+public class TestSuite implements Test {
+
+	private Vector<Test> fTests= new Vector<Test>(10);
+	private String fName;
+
+    /**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite() {
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class with the given name.
+	 * @see TestSuite#TestSuite(Class)
+	 */
+	public TestSuite(Class theClass, String name) {
+		this(theClass);
+		setName(name);
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class. Adds all the methods
+	 * starting with "test" as test cases to the suite.
+	 * Parts of this method was written at 2337 meters in the Huffihutte,
+	 * Kanton Uri
+	 */
+	 public TestSuite(final Class theClass) {
+		fName= theClass.getName();
+		try {
+			getTestConstructor(theClass); // Avoid generating multiple error messages
+		} catch (NoSuchMethodException e) {
+			addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
+			return;
+		}
+
+		if (!Modifier.isPublic(theClass.getModifiers())) {
+			addTest(warning("Class "+theClass.getName()+" is not public"));
+			return;
+		}
+
+		Class superClass= theClass;
+		Vector<String> names= new Vector<String>();
+		while (Test.class.isAssignableFrom(superClass)) {
+			Method[] methods= superClass.getDeclaredMethods();
+			for (int i= 0; i < methods.length; i++) {
+				addTestMethod(methods[i], names, theClass);
+			}
+			superClass= superClass.getSuperclass();
+		}
+		if (fTests.size() == 0)
+			addTest(warning("No tests found in "+theClass.getName()));
+	}
+	
+   	/**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite(String name) {
+		setName(name);
+	}
+	
+	/**
+	 * Adds a test to the suite.
+	 */
+	public void addTest(Test test) {
+		fTests.addElement(test);
+	}
+
+	/**
+	 * Adds the tests from the given class to the suite
+	 */
+	public void addTestSuite(Class testClass) {
+		addTest(new TestSuite(testClass));
+	}
+
+	private void addTestMethod(Method m, Vector<String> names, Class theClass) {
+		String name= m.getName();
+		if (names.contains(name))
+			return;
+		if (! isPublicTestMethod(m)) {
+			if (isTestMethod(m))
+				addTest(warning("Test method isn't public: "+m.getName()));
+			return;
+		}
+		names.addElement(name);
+		addTest(createTest(theClass, name));
+	}
+
+	/**
+	 * ...as the moon sets over the early morning Merlin, Oregon
+	 * mountains, our intrepid adventurers type...
+	 */
+	static public Test createTest(Class theClass, String name) {
+		Constructor constructor;
+		try {
+			constructor= getTestConstructor(theClass);
+		} catch (NoSuchMethodException e) {
+			return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
+		}
+		Object test;
+		try {
+			if (constructor.getParameterTypes().length == 0) {
+				test= constructor.newInstance(new Object[0]);
+				if (test instanceof TestCase)
+					((TestCase) test).setName(name);
+			} else {
+				test= constructor.newInstance(new Object[]{name});
+			}
+		} catch (InstantiationException e) {
+			return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
+		} catch (InvocationTargetException e) {
+			return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
+		} catch (IllegalAccessException e) {
+			return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
+		}
+		return (Test) test;
+	}
+
+	/**
+	 * Converts the stack trace into a string
+	 */
+	private static String exceptionToString(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		return stringWriter.toString();
+
+	}
+	
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public int countTestCases() {
+		int count= 0;
+		for (Enumeration e= tests(); e.hasMoreElements(); ) {
+			Test test= (Test)e.nextElement();
+			count= count + test.countTestCases();
+		}
+		return count;
+	}
+	
+	/**
+	 * Gets a constructor which takes a single String as
+	 * its argument or a no arg constructor.
+	 */
+	public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
+		Class[] args= { String.class };
+		try {
+			return theClass.getConstructor(args);	
+		} catch (NoSuchMethodException e) {
+			// fall through
+		}
+		return theClass.getConstructor(new Class[0]);
+	}
+
+	private boolean isPublicTestMethod(Method m) {
+		return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+	 }
+	 
+	private boolean isTestMethod(Method m) {
+		String name= m.getName();
+		Class[] parameters= m.getParameterTypes();
+		Class returnType= m.getReturnType();
+		return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
+	 }
+	 
+	/**
+	 * Runs the tests and collects their result in a TestResult.
+	 */
+	public void run(TestResult result) {
+		for (Enumeration e= tests(); e.hasMoreElements(); ) {
+	  		if (result.shouldStop() )
+	  			break;
+			Test test= (Test)e.nextElement();
+			runTest(test, result);
+		}
+	}
+
+	public void runTest(Test test, TestResult result) {
+		test.run(result);
+	}
+
+	/**
+	 * Returns the test at the given index
+	 */
+	public Test testAt(int index) {
+		return (Test)fTests.elementAt(index);
+	}
+	
+	/**
+	 * Returns the number of tests in this suite
+	 */
+	public int testCount() {
+		return fTests.size();
+	}
+	
+	/**
+	 * Returns the tests as an enumeration
+	 */
+	public Enumeration tests() {
+		return fTests.elements();
+	}
+	
+	/**
+	 */
+	public String toString() {
+		if (getName() != null)
+			return getName();
+		return super.toString();
+	 }
+	 
+	/**
+	 * Sets the name of the suite.
+	 * @param name The name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+
+	/**
+	 * Returns the name of the suite. Not all
+	 * test suites have a name and this method
+	 * can return null.
+	 */
+	public String getName() {
+		return fName;
+	}
+
+	/**
+	 * Returns a test which will fail and log a warning message.
+	 */
+	private static Test warning(final String message) {
+		return new TestCase("warning") {
+			protected void runTest() {
+				fail(message);
+			}
+		};
+	}
+}
diff --git a/dx/src/junit/runner/BaseTestRunner.java b/dx/src/junit/runner/BaseTestRunner.java
new file mode 100644
index 0000000..39c8c2f
--- /dev/null
+++ b/dx/src/junit/runner/BaseTestRunner.java
@@ -0,0 +1,323 @@
+package junit.runner;
+
+import junit.framework.*;
+import java.lang.reflect.*;
+import java.text.NumberFormat;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Base class for all test runners.
+ * This class was born live on stage in Sardinia during XP2000.
+ */
+public abstract class BaseTestRunner implements TestListener {
+	public static final String SUITE_METHODNAME= "suite";
+
+	private static Properties fPreferences;
+	static int fgMaxMessageLength= 500;
+	static boolean fgFilterStack= true;
+	boolean fLoading= true;
+
+    /*
+    * Implementation of TestListener
+    */
+	public synchronized void startTest(Test test) {
+		testStarted(test.toString());
+	}
+
+	protected static void setPreferences(Properties preferences) {
+		fPreferences= preferences;
+	}
+
+	protected static Properties getPreferences() {
+		if (fPreferences == null) {
+			fPreferences= new Properties();
+	 		fPreferences.put("loading", "true");
+ 			fPreferences.put("filterstack", "true");
+  			readPreferences();
+		}
+		return fPreferences;
+	}
+
+	public static void savePreferences() throws IOException {
+		FileOutputStream fos= new FileOutputStream(getPreferencesFile());
+		try {
+			getPreferences().store(fos, "");
+		} finally {
+			fos.close();
+		}
+	}
+
+	public void setPreference(String key, String value) {
+		getPreferences().setProperty(key, value);
+	}
+
+	public synchronized void endTest(Test test) {
+		testEnded(test.toString());
+	}
+
+	public synchronized void addError(final Test test, final Throwable t) {
+		testFailed(TestRunListener.STATUS_ERROR, test, t);
+	}
+
+	public synchronized void addFailure(final Test test, final AssertionFailedError t) {
+		testFailed(TestRunListener.STATUS_FAILURE, test, t);
+	}
+
+	// TestRunListener implementation
+
+	public abstract void testStarted(String testName);
+
+	public abstract void testEnded(String testName);
+
+	public abstract void testFailed(int status, Test test, Throwable t);
+
+	/**
+	 * Returns the Test corresponding to the given suite. This is
+	 * a template method, subclasses override runFailed(), clearStatus().
+	 */
+	public Test getTest(String suiteClassName) {
+		if (suiteClassName.length() <= 0) {
+			clearStatus();
+			return null;
+		}
+		Class testClass= null;
+		try {
+			testClass= loadSuiteClass(suiteClassName);
+		} catch (ClassNotFoundException e) {
+			String clazz= e.getMessage();
+			if (clazz == null)
+				clazz= suiteClassName;
+			runFailed("Class not found \""+clazz+"\"");
+			return null;
+		} catch(Exception e) {
+			runFailed("Error: "+e.toString());
+			return null;
+		}
+		Method suiteMethod= null;
+		try {
+			suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
+	 	} catch(Exception e) {
+	 		// try to extract a test suite automatically
+			clearStatus();
+			return new TestSuite(testClass);
+		}
+		if (! Modifier.isStatic(suiteMethod.getModifiers())) {
+			runFailed("Suite() method must be static");
+			return null;
+		}
+		Test test= null;
+		try {
+			test= (Test)suiteMethod.invoke(null, (Object[]) new Class[0]); // static method
+			if (test == null)
+				return test;
+		}
+		catch (InvocationTargetException e) {
+			runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+			return null;
+		}
+		catch (IllegalAccessException e) {
+			runFailed("Failed to invoke suite():" + e.toString());
+			return null;
+		}
+
+		clearStatus();
+		return test;
+	}
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 */
+	public String elapsedTimeAsString(long runTime) {
+		return NumberFormat.getInstance().format((double)runTime/1000);
+	}
+
+	/**
+	 * Processes the command line arguments and
+	 * returns the name of the suite class to run or null
+	 */
+	protected String processArguments(String[] args) {
+		String suiteName= null;
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-noloading")) {
+				setLoading(false);
+			} else if (args[i].equals("-nofilterstack")) {
+				fgFilterStack= false;
+			} else if (args[i].equals("-c")) {
+				if (args.length > i+1)
+					suiteName= extractClassName(args[i+1]);
+				else
+					System.out.println("Missing Test class name");
+				i++;
+			} else {
+				suiteName= args[i];
+			}
+		}
+		return suiteName;
+	}
+
+	/**
+	 * Sets the loading behaviour of the test runner
+	 */
+	public void setLoading(boolean enable) {
+		fLoading= enable;
+	}
+	/**
+	 * Extract the class name from a String in VA/Java style
+	 */
+	public String extractClassName(String className) {
+		if(className.startsWith("Default package for"))
+			return className.substring(className.lastIndexOf(".")+1);
+		return className;
+	}
+
+	/**
+	 * Truncates a String to the maximum length.
+	 */
+	public static String truncate(String s) {
+		if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
+			s= s.substring(0, fgMaxMessageLength)+"...";
+		return s;
+	}
+
+	/**
+	 * Override to define how to handle a failed loading of
+	 * a test suite.
+	 */
+	protected abstract void runFailed(String message);
+
+	/**
+	 * Returns the loaded Class for a suite name.
+	 */
+	protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+		return getLoader().load(suiteClassName);
+	}
+
+	/**
+	 * Clears the status message.
+	 */
+	protected void clearStatus() { // Belongs in the GUI TestRunner class
+	}
+
+	/**
+	 * Returns the loader to be used.
+	 */
+	public TestSuiteLoader getLoader() {
+		if (useReloadingTestSuiteLoader())
+			return new ReloadingTestSuiteLoader();
+		return new StandardTestSuiteLoader();
+	}
+
+	protected boolean useReloadingTestSuiteLoader() {
+		return getPreference("loading").equals("true") && !inVAJava() && fLoading;
+	}
+
+	private static File getPreferencesFile() {
+	 	String home= System.getProperty("user.home");
+ 		return new File(home, "junit.properties");
+ 	}
+
+ 	private static void readPreferences() {
+ 		InputStream is= null;
+ 		try {
+ 			is= new FileInputStream(getPreferencesFile());
+ 			setPreferences(new Properties(getPreferences()));
+			getPreferences().load(is);
+		} catch (IOException e) {
+			try {
+				if (is != null)
+					is.close();
+			} catch (IOException e1) {
+			}
+		}
+ 	}
+
+ 	public static String getPreference(String key) {
+ 		return getPreferences().getProperty(key);
+ 	}
+
+ 	public static int getPreference(String key, int dflt) {
+ 		String value= getPreference(key);
+ 		int intValue= dflt;
+ 		if (value == null)
+ 			return intValue;
+ 		try {
+ 			intValue= Integer.parseInt(value);
+ 	 	} catch (NumberFormatException ne) {
+ 		}
+ 		return intValue;
+ 	}
+
+ 	public static boolean inVAJava() {
+		try {
+			Class.forName("com.ibm.uvm.tools.DebugSupport");
+		}
+		catch (Exception e) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Returns a filtered stack trace
+	 */
+	public static String getFilteredTrace(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		String trace= buffer.toString();
+		return BaseTestRunner.getFilteredTrace(trace);
+	}
+
+	/**
+	 * Filters stack frames from internal JUnit classes
+	 */
+	public static String getFilteredTrace(String stack) {
+		if (showStackRaw())
+			return stack;
+
+		StringWriter sw= new StringWriter();
+		PrintWriter pw= new PrintWriter(sw);
+		StringReader sr= new StringReader(stack);
+		BufferedReader br= new BufferedReader(sr);
+
+		String line;
+		try {
+			while ((line= br.readLine()) != null) {
+				if (!filterLine(line))
+					pw.println(line);
+			}
+		} catch (Exception IOException) {
+			return stack; // return the stack unfiltered
+		}
+		return sw.toString();
+	}
+
+	protected static boolean showStackRaw() {
+		return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+	}
+
+	static boolean filterLine(String line) {
+		String[] patterns= new String[] {
+			"junit.framework.TestCase",
+			"junit.framework.TestResult",
+			"junit.framework.TestSuite",
+			"junit.framework.Assert.", // don't filter AssertionFailure
+			"junit.swingui.TestRunner",
+			"junit.awtui.TestRunner",
+			"junit.textui.TestRunner",
+			"java.lang.reflect.Method.invoke("
+		};
+		for (int i= 0; i < patterns.length; i++) {
+			if (line.indexOf(patterns[i]) > 0)
+				return true;
+		}
+		return false;
+	}
+
+ 	static {
+ 		fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
+ 	}
+
+}
diff --git a/dx/src/junit/runner/ClassPathTestCollector.java b/dx/src/junit/runner/ClassPathTestCollector.java
new file mode 100644
index 0000000..24bf1e9
--- /dev/null
+++ b/dx/src/junit/runner/ClassPathTestCollector.java
@@ -0,0 +1,80 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * An implementation of a TestCollector that consults the
+ * class path. It considers all classes on the class path
+ * excluding classes in JARs. It leaves it up to subclasses
+ * to decide whether a class is a runnable Test.
+ *
+ * @see TestCollector
+ */
+public abstract class ClassPathTestCollector implements TestCollector {
+	
+	static final int SUFFIX_LENGTH= ".class".length();
+	
+	public ClassPathTestCollector() {
+	}
+	
+	public Enumeration collectTests() {
+		String classPath= System.getProperty("java.class.path");
+		Hashtable result = collectFilesInPath(classPath);
+		return result.elements();
+	}
+
+	public Hashtable collectFilesInPath(String classPath) {
+		Hashtable result= collectFilesInRoots(splitClassPath(classPath));
+		return result;
+	}
+	
+	Hashtable collectFilesInRoots(Vector roots) {
+                Hashtable<String,String> result= new Hashtable<String,String>(100);
+		Enumeration e= roots.elements();
+		while (e.hasMoreElements()) 
+			gatherFiles(new File((String)e.nextElement()), "", result);
+		return result;
+	}
+
+    void gatherFiles(File classRoot, String classFileName, Hashtable<String,String> result) {
+		File thisRoot= new File(classRoot, classFileName);
+		if (thisRoot.isFile()) {
+			if (isTestClass(classFileName)) {
+				String className= classNameFromFile(classFileName);
+				result.put(className, className);
+			}
+			return;
+		}		
+		String[] contents= thisRoot.list();
+		if (contents != null) { 
+			for (int i= 0; i < contents.length; i++) 
+				gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);		
+		}
+	}
+	
+	Vector splitClassPath(String classPath) {
+		Vector<String> result= new Vector<String>();
+		String separator= System.getProperty("path.separator");
+		StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
+		while (tokenizer.hasMoreTokens()) 
+			result.addElement(tokenizer.nextToken());
+		return result;
+	}
+	
+	protected boolean isTestClass(String classFileName) {
+		return 
+			classFileName.endsWith(".class") && 
+			classFileName.indexOf('$') < 0 &&
+			classFileName.indexOf("Test") > 0;
+	}
+	
+	protected String classNameFromFile(String classFileName) {
+		// convert /a/b.class to a.b
+		String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
+		String s2= s.replace(File.separatorChar, '.');
+		if (s2.startsWith("."))
+			return s2.substring(1);
+		return s2;
+	}	
+}
diff --git a/dx/src/junit/runner/FailureDetailView.java b/dx/src/junit/runner/FailureDetailView.java
new file mode 100644
index 0000000..762326b
--- /dev/null
+++ b/dx/src/junit/runner/FailureDetailView.java
@@ -0,0 +1,23 @@
+package junit.runner;
+
+import java.awt.Component; 
+
+import junit.framework.*;
+
+/**
+ * A view to show a details about a failure
+ */
+public interface FailureDetailView {
+	/**
+	 * Returns the component used to present the TraceView
+	 */
+	public Component getComponent();
+	/**
+	 * Shows details of a TestFailure
+	 */
+	public void showFailure(TestFailure failure);
+	/**
+	 * Clears the view
+	 */
+	public void clear();
+}
diff --git a/dx/src/junit/runner/LoadingTestCollector.java b/dx/src/junit/runner/LoadingTestCollector.java
new file mode 100644
index 0000000..b138359
--- /dev/null
+++ b/dx/src/junit/runner/LoadingTestCollector.java
@@ -0,0 +1,69 @@
+package junit.runner;
+
+import java.lang.reflect.*;
+import junit.runner.*;
+import junit.framework.*;
+
+/**
+ * An implementation of a TestCollector that loads
+ * all classes on the class path and tests whether
+ * it is assignable from Test or provides a static suite method.
+ * @see TestCollector
+ */
+public class LoadingTestCollector extends ClassPathTestCollector {
+	
+	TestCaseClassLoader fLoader;
+	
+	public LoadingTestCollector() {
+		fLoader= new TestCaseClassLoader();
+	}
+	
+	protected boolean isTestClass(String classFileName) {	
+		try {
+			if (classFileName.endsWith(".class")) {
+				Class testClass= classFromFile(classFileName);
+				return (testClass != null) && isTestClass(testClass);
+			}
+		} 
+		catch (ClassNotFoundException expected) {
+		}
+		catch (NoClassDefFoundError notFatal) {
+		} 
+		return false;
+	}
+	
+	Class classFromFile(String classFileName) throws ClassNotFoundException {
+		String className= classNameFromFile(classFileName);
+		if (!fLoader.isExcluded(className))
+			return fLoader.loadClass(className, false);
+		return null;
+	}
+	
+	boolean isTestClass(Class testClass) {
+		if (hasSuiteMethod(testClass))
+			return true;
+		if (Test.class.isAssignableFrom(testClass) &&
+			Modifier.isPublic(testClass.getModifiers()) &&
+			hasPublicConstructor(testClass)) 
+			return true;
+		return false;
+	}
+	
+	boolean hasSuiteMethod(Class testClass) {
+		try {
+			testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
+	 	} catch(Exception e) {
+	 		return false;
+		}
+		return true;
+	}
+	
+	boolean hasPublicConstructor(Class testClass) {
+		try {
+			TestSuite.getTestConstructor(testClass);
+		} catch(NoSuchMethodException e) {
+			return false;
+		}
+		return true;
+	}
+}
diff --git a/dx/src/junit/runner/ReloadingTestSuiteLoader.java b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
new file mode 100644
index 0000000..f7ef919
--- /dev/null
+++ b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * A TestSuite loader that can reload classes.
+ */
+public class ReloadingTestSuiteLoader implements TestSuiteLoader {
+	
+	public Class load(String suiteClassName) throws ClassNotFoundException {
+		return createLoader().loadClass(suiteClassName, true);
+	}
+	
+	public Class reload(Class aClass) throws ClassNotFoundException {
+		return createLoader().loadClass(aClass.getName(), true);
+	}
+	
+	protected TestCaseClassLoader createLoader() {
+		return new TestCaseClassLoader();
+	}
+}
diff --git a/dx/src/junit/runner/SimpleTestCollector.java b/dx/src/junit/runner/SimpleTestCollector.java
new file mode 100644
index 0000000..9d1956a
--- /dev/null
+++ b/dx/src/junit/runner/SimpleTestCollector.java
@@ -0,0 +1,20 @@
+package junit.runner;
+
+/**
+ * An implementation of a TestCollector that considers
+ * a class to be a test class when it contains the
+ * pattern "Test" in its name
+ * @see TestCollector
+ */
+public class SimpleTestCollector extends ClassPathTestCollector {
+	
+	public SimpleTestCollector() {
+	}
+	
+	protected boolean isTestClass(String classFileName) {
+		return 
+			classFileName.endsWith(".class") && 
+			classFileName.indexOf('$') < 0 &&
+			classFileName.indexOf("Test") > 0;
+	}
+}
diff --git a/dx/src/junit/runner/Sorter.java b/dx/src/junit/runner/Sorter.java
new file mode 100644
index 0000000..e614ab4
--- /dev/null
+++ b/dx/src/junit/runner/Sorter.java
@@ -0,0 +1,38 @@
+package junit.runner;
+
+import java.util.*;
+
+import junit.runner.*;
+
+/**
+ * A custom quick sort with support to customize the swap behaviour.
+ * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
+ * classes because of the JDK 1.1.7 compatibility.
+ */
+public class Sorter {
+	public static interface Swapper {
+		public void swap(Vector values, int left, int right);
+	}
+		
+	public static void sortStrings(Vector values , int left, int right, Swapper swapper) { 
+		int oleft= left;
+		int oright= right;
+		String mid= (String)values.elementAt((left + right) / 2); 
+		do { 
+			while (((String)(values.elementAt(left))).compareTo(mid) < 0)  
+				left++; 
+			while (mid.compareTo((String)(values.elementAt(right))) < 0)  
+				right--; 
+			if (left <= right) {
+				swapper.swap(values, left, right); 
+				left++; 
+				right--; 
+			} 
+		} while (left <= right);
+		
+		if (oleft < right) 
+			sortStrings(values, oleft, right, swapper); 
+		if (left < oright) 
+			 sortStrings(values, left, oright, swapper); 
+	}
+}
diff --git a/dx/src/junit/runner/StandardTestSuiteLoader.java b/dx/src/junit/runner/StandardTestSuiteLoader.java
new file mode 100644
index 0000000..e2bd765
--- /dev/null
+++ b/dx/src/junit/runner/StandardTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * The standard test suite loader. It can only load the same class once.
+ */
+public class StandardTestSuiteLoader implements TestSuiteLoader {
+	/**
+	 * Uses the system class loader to load the test class
+	 */
+	public Class load(String suiteClassName) throws ClassNotFoundException {
+		return Class.forName(suiteClassName);
+	}
+	/**
+	 * Uses the system class loader to load the test class
+	 */
+	public Class reload(Class aClass) throws ClassNotFoundException {
+		return aClass;
+	}
+}
diff --git a/dx/src/junit/runner/TestCaseClassLoader.java b/dx/src/junit/runner/TestCaseClassLoader.java
new file mode 100644
index 0000000..543641a
--- /dev/null
+++ b/dx/src/junit/runner/TestCaseClassLoader.java
@@ -0,0 +1,226 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+import java.net.URL;
+import java.util.zip.*;
+
+/**
+ * A custom class loader which enables the reloading
+ * of classes for each test run. The class loader
+ * can be configured with a list of package paths that
+ * should be excluded from loading. The loading
+ * of these packages is delegated to the system class
+ * loader. They will be shared across test runs.
+ * <p>
+ * The list of excluded package paths is specified in
+ * a properties file "excluded.properties" that is located in 
+ * the same place as the TestCaseClassLoader class.
+ * <p>
+ * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
+ * from jar files.
+ */
+
+
+public class TestCaseClassLoader extends ClassLoader {
+	/** scanned class path */
+	private Vector<String> fPathItems;
+	/** default excluded paths */
+	private String[] defaultExclusions= {
+		"junit.framework.", 
+		"junit.extensions.", 
+		"junit.runner."
+	};
+	/** name of excluded properties file */
+	static final String EXCLUDED_FILE= "excluded.properties";
+	/** excluded paths */
+	private Vector<String> fExcluded;
+	 
+	/**
+	 * Constructs a TestCaseLoader. It scans the class path
+	 * and the excluded package paths
+	 */
+	public TestCaseClassLoader() {
+		this(System.getProperty("java.class.path"));
+	}
+	
+	/**
+	 * Constructs a TestCaseLoader. It scans the class path
+	 * and the excluded package paths
+	 */
+	public TestCaseClassLoader(String classPath) {
+		scanPath(classPath);
+		readExcludedPackages();
+	}
+
+	private void scanPath(String classPath) {
+		String separator= System.getProperty("path.separator");
+		fPathItems= new Vector<String>(10);
+		StringTokenizer st= new StringTokenizer(classPath, separator);
+		while (st.hasMoreTokens()) {
+			fPathItems.addElement(st.nextToken());
+		}
+	}
+	
+	public URL getResource(String name) {
+		return ClassLoader.getSystemResource(name);
+	}
+	
+	public InputStream getResourceAsStream(String name) {
+		return ClassLoader.getSystemResourceAsStream(name);
+	} 
+	
+	public boolean isExcluded(String name) {
+		for (int i= 0; i < fExcluded.size(); i++) {
+			if (name.startsWith((String) fExcluded.elementAt(i))) {
+				return true;
+			}
+		}
+		return false;	
+	}
+	
+	public synchronized Class loadClass(String name, boolean resolve)
+		throws ClassNotFoundException {
+			
+		Class c= findLoadedClass(name);
+		if (c != null)
+			return c;
+		//
+		// Delegate the loading of excluded classes to the
+		// standard class loader.
+		//
+		if (isExcluded(name)) {
+			try {
+				c= findSystemClass(name);
+				return c;
+			} catch (ClassNotFoundException e) {
+				// keep searching
+			}
+		}
+		if (c == null) {
+			byte[] data= lookupClassData(name);
+			if (data == null)
+				throw new ClassNotFoundException();
+			c= defineClass(name, data, 0, data.length);
+		}
+		if (resolve) 
+			resolveClass(c);
+		return c;
+	}
+	
+	private byte[] lookupClassData(String className) throws ClassNotFoundException {
+		byte[] data= null;
+		for (int i= 0; i < fPathItems.size(); i++) {
+			String path= (String) fPathItems.elementAt(i);
+			String fileName= className.replace('.', '/')+".class";
+			if (isJar(path)) {
+				data= loadJarData(path, fileName);
+			} else {
+				data= loadFileData(path, fileName);
+			}
+			if (data != null)
+				return data;
+		}
+		throw new ClassNotFoundException(className);
+	}
+		
+	boolean isJar(String pathEntry) {
+		return pathEntry.endsWith(".jar") ||
+                       pathEntry.endsWith(".zip") ||
+                       pathEntry.endsWith(".apk");
+	}
+
+	private byte[] loadFileData(String path, String fileName) {
+		File file= new File(path, fileName);
+		if (file.exists()) { 
+			return getClassData(file);
+		}
+		return null;
+	}
+	
+	private byte[] getClassData(File f) {
+		try {
+			FileInputStream stream= new FileInputStream(f);
+			ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
+			byte[] b= new byte[1000];
+			int n;
+			while ((n= stream.read(b)) != -1) 
+				out.write(b, 0, n);
+			stream.close();
+			out.close();
+			return out.toByteArray();
+
+		} catch (IOException e) {
+		}
+		return null;
+	}
+
+	private byte[] loadJarData(String path, String fileName) {
+		ZipFile zipFile= null;
+		InputStream stream= null;
+		File archive= new File(path);
+		if (!archive.exists())
+			return null;
+		try {
+			zipFile= new ZipFile(archive);
+		} catch(IOException io) {
+			return null;
+		}
+		ZipEntry entry= zipFile.getEntry(fileName);
+		if (entry == null)
+			return null;
+		int size= (int) entry.getSize();
+		try {
+			stream= zipFile.getInputStream(entry);
+			byte[] data= new byte[size];
+			int pos= 0;
+			while (pos < size) {
+				int n= stream.read(data, pos, data.length - pos);
+				pos += n;
+			}
+			zipFile.close();
+			return data;
+		} catch (IOException e) {
+		} finally {
+			try {
+				if (stream != null)
+					stream.close();
+			} catch (IOException e) {
+			}
+		}
+		return null;
+	}
+	
+	private void readExcludedPackages() {		
+		fExcluded= new Vector<String>(10);
+		for (int i= 0; i < defaultExclusions.length; i++)
+			fExcluded.addElement(defaultExclusions[i]);
+			
+		InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
+		if (is == null) 
+			return;
+		Properties p= new Properties();
+		try {
+			p.load(is);
+		}
+		catch (IOException e) {
+			return;
+		} finally {
+			try {
+				is.close();
+			} catch (IOException e) {
+			}
+		}
+		for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
+			String key= (String)e.nextElement();
+			if (key.startsWith("excluded.")) {
+				String path= p.getProperty(key);
+				path= path.trim();
+				if (path.endsWith("*"))
+					path= path.substring(0, path.length()-1);
+				if (path.length() > 0) 
+					fExcluded.addElement(path);				
+			}
+		}
+	}
+}
diff --git a/dx/src/junit/runner/TestCollector.java b/dx/src/junit/runner/TestCollector.java
new file mode 100644
index 0000000..73efb4e
--- /dev/null
+++ b/dx/src/junit/runner/TestCollector.java
@@ -0,0 +1,16 @@
+package junit.runner;
+
+import java.util.*;
+
+
+/**
+ * Collects Test class names to be presented
+ * by the TestSelector. 
+ * @see TestSelector
+ */
+public interface TestCollector {
+	/**
+	 * Returns an enumeration of Strings with qualified class names
+	 */
+	public Enumeration collectTests();
+}
diff --git a/dx/src/junit/runner/TestRunListener.java b/dx/src/junit/runner/TestRunListener.java
new file mode 100644
index 0000000..b11ef07
--- /dev/null
+++ b/dx/src/junit/runner/TestRunListener.java
@@ -0,0 +1,19 @@
+package junit.runner;
+/**
+ * A listener interface for observing the
+ * execution of a test run. Unlike TestListener,
+ * this interface using only primitive objects,
+ * making it suitable for remote test execution.
+ */
+ public interface TestRunListener {
+     /* test status constants*/
+     public static final int STATUS_ERROR= 1;
+     public static final int STATUS_FAILURE= 2;
+
+     public void testRunStarted(String testSuiteName, int testCount);
+     public void testRunEnded(long elapsedTime);
+     public void testRunStopped(long elapsedTime);
+     public void testStarted(String testName);
+     public void testEnded(String testName);
+     public void testFailed(int status, String testName, String trace);
+}
diff --git a/dx/src/junit/runner/TestSuiteLoader.java b/dx/src/junit/runner/TestSuiteLoader.java
new file mode 100644
index 0000000..39a4cf7
--- /dev/null
+++ b/dx/src/junit/runner/TestSuiteLoader.java
@@ -0,0 +1,9 @@
+package junit.runner;
+
+/**
+ * An interface to define how a test suite should be loaded.
+ */
+public interface TestSuiteLoader {
+	abstract public Class load(String suiteClassName) throws ClassNotFoundException;
+	abstract public Class reload(Class aClass) throws ClassNotFoundException;
+}
diff --git a/dx/src/junit/runner/Version.java b/dx/src/junit/runner/Version.java
new file mode 100644
index 0000000..b4541ab
--- /dev/null
+++ b/dx/src/junit/runner/Version.java
@@ -0,0 +1,14 @@
+package junit.runner;
+
+/**
+ * This class defines the current version of JUnit
+ */
+public class Version {
+	private Version() {
+		// don't instantiate
+	}
+
+	public static String id() {
+		return "3.8.1";
+	}
+}
diff --git a/dx/src/junit/runner/excluded.properties b/dx/src/junit/runner/excluded.properties
new file mode 100644
index 0000000..3284628
--- /dev/null
+++ b/dx/src/junit/runner/excluded.properties
@@ -0,0 +1,12 @@
+#
+# The list of excluded package paths for the TestCaseClassLoader
+#
+excluded.0=sun.*
+excluded.1=com.sun.*
+excluded.2=org.omg.*
+excluded.3=javax.*
+excluded.4=sunw.*
+excluded.5=java.*
+excluded.6=org.w3c.dom.*
+excluded.7=org.xml.sax.*
+excluded.8=net.jini.*
diff --git a/dx/src/junit/runner/logo.gif b/dx/src/junit/runner/logo.gif
new file mode 100644
index 0000000..d0e1547
--- /dev/null
+++ b/dx/src/junit/runner/logo.gif
Binary files differ
diff --git a/dx/src/junit/runner/smalllogo.gif b/dx/src/junit/runner/smalllogo.gif
new file mode 100644
index 0000000..7b25eaf
--- /dev/null
+++ b/dx/src/junit/runner/smalllogo.gif
Binary files differ
diff --git a/dx/src/junit/textui/ResultPrinter.java b/dx/src/junit/textui/ResultPrinter.java
new file mode 100644
index 0000000..1ebb7a1
--- /dev/null
+++ b/dx/src/junit/textui/ResultPrinter.java
@@ -0,0 +1,139 @@
+
+package junit.textui;
+
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+public class ResultPrinter implements TestListener {
+	PrintStream fWriter;
+	int fColumn= 0;
+	
+	public ResultPrinter(PrintStream writer) {
+		fWriter= writer;
+	}
+	
+	/* API for use by textui.TestRunner
+	 */
+
+	synchronized void print(TestResult result, long runTime) {
+		printHeader(runTime);
+	    printErrors(result);
+	    printFailures(result);
+	    printFooter(result);
+	}
+
+	void printWaitPrompt() {
+		getWriter().println();
+		getWriter().println("<RETURN> to continue");
+	}
+	
+	/* Internal methods 
+	 */
+
+	protected void printHeader(long runTime) {
+		getWriter().println();
+		getWriter().println("Time: "+elapsedTimeAsString(runTime));
+	}
+	
+	protected void printErrors(TestResult result) {
+		printDefects(result.errors(), result.errorCount(), "error");
+	}
+	
+	protected void printFailures(TestResult result) {
+		printDefects(result.failures(), result.failureCount(), "failure");
+	}
+	
+	protected void printDefects(Enumeration booBoos, int count, String type) {
+		if (count == 0) return;
+		if (count == 1)
+			getWriter().println("There was " + count + " " + type + ":");
+		else
+			getWriter().println("There were " + count + " " + type + "s:");
+		for (int i= 1; booBoos.hasMoreElements(); i++) {
+			printDefect((TestFailure) booBoos.nextElement(), i);
+		}
+	}
+	
+	public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+		printDefectHeader(booBoo, count);
+		printDefectTrace(booBoo);
+	}
+
+	protected void printDefectHeader(TestFailure booBoo, int count) {
+		// I feel like making this a println, then adding a line giving the throwable a chance to print something
+		// before we get to the stack trace.
+		getWriter().print(count + ") " + booBoo.failedTest());
+	}
+
+	protected void printDefectTrace(TestFailure booBoo) {
+		getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+	}
+
+	protected void printFooter(TestResult result) {
+		if (result.wasSuccessful()) {
+			getWriter().println();
+			getWriter().print("OK");
+			getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+
+		} else {
+			getWriter().println();
+			getWriter().println("FAILURES!!!");
+			getWriter().println("Tests run: "+result.runCount()+ 
+				         ",  Failures: "+result.failureCount()+
+				         ",  Errors: "+result.errorCount());
+		}
+	    getWriter().println();
+	}
+
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 * Duplicated from BaseTestRunner. Fix it.
+	 */
+	protected String elapsedTimeAsString(long runTime) {
+		return NumberFormat.getInstance().format((double)runTime/1000);
+	}
+
+	public PrintStream getWriter() {
+		return fWriter;
+	}
+	/**
+	 * @see junit.framework.TestListener#addError(Test, Throwable)
+	 */
+	public void addError(Test test, Throwable t) {
+		getWriter().print("E");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+	 */
+	public void addFailure(Test test, AssertionFailedError t) {
+		getWriter().print("F");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#endTest(Test)
+	 */
+	public void endTest(Test test) {
+	}
+
+	/**
+	 * @see junit.framework.TestListener#startTest(Test)
+	 */
+	public void startTest(Test test) {
+		getWriter().print(".");
+		if (fColumn++ >= 40) {
+			getWriter().println();
+			fColumn= 0;
+		}
+	}
+
+}
diff --git a/dx/src/junit/textui/TestRunner.java b/dx/src/junit/textui/TestRunner.java
new file mode 100644
index 0000000..8bdc325
--- /dev/null
+++ b/dx/src/junit/textui/TestRunner.java
@@ -0,0 +1,189 @@
+package junit.textui;
+
+
+import java.io.PrintStream;
+
+import junit.framework.*;
+import junit.runner.*;
+
+/**
+ * A command line based tool to run tests.
+ * <pre>
+ * java junit.textui.TestRunner [-wait] TestCaseClass
+ * </pre>
+ * TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it 
+ * will be invoked and the returned test is run. Otherwise all 
+ * the methods starting with "test" having no arguments are run.
+ * <p>
+ * When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.
+ * <p>
+ * TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end. 
+ */
+public class TestRunner extends BaseTestRunner {
+	private ResultPrinter fPrinter;
+	
+	public static final int SUCCESS_EXIT= 0;
+	public static final int FAILURE_EXIT= 1;
+	public static final int EXCEPTION_EXIT= 2;
+
+	/**
+	 * Constructs a TestRunner.
+	 */
+	public TestRunner() {
+		this(System.out);
+	}
+
+	/**
+	 * Constructs a TestRunner using the given stream for all the output
+	 */
+	public TestRunner(PrintStream writer) {
+		this(new ResultPrinter(writer));
+	}
+	
+	/**
+	 * Constructs a TestRunner using the given ResultPrinter all the output
+	 */
+	public TestRunner(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+	
+	/**
+	 * Runs a suite extracted from a TestCase subclass.
+	 */
+	static public void run(Class testClass) {
+		run(new TestSuite(testClass));
+	}
+
+	/**
+	 * Runs a single test and collects its results.
+	 * This method can be used to start a test run
+	 * from your program.
+	 * <pre>
+	 * public static void main (String[] args) {
+	 *     test.textui.TestRunner.run(suite());
+	 * }
+	 * </pre>
+	 */
+	static public TestResult run(Test test) {
+		TestRunner runner= new TestRunner();
+		return runner.doRun(test);
+	}
+
+	/**
+	 * Runs a single test and waits until the user
+	 * types RETURN.
+	 */
+	static public void runAndWait(Test suite) {
+		TestRunner aTestRunner= new TestRunner();
+		aTestRunner.doRun(suite, true);
+	}
+
+	/**
+	 * Always use the StandardTestSuiteLoader. Overridden from
+	 * BaseTestRunner.
+	 */
+	public TestSuiteLoader getLoader() {
+		return new StandardTestSuiteLoader();
+	}
+
+	public void testFailed(int status, Test test, Throwable t) {
+	}
+	
+	public void testStarted(String testName) {
+	}
+	
+	public void testEnded(String testName) {
+	}
+
+	/**
+	 * Creates the TestResult to be used for the test run.
+	 */
+	protected TestResult createTestResult() {
+		return new TestResult();
+	}
+	
+	public TestResult doRun(Test test) {
+		return doRun(test, false);
+	}
+	
+	public TestResult doRun(Test suite, boolean wait) {
+		TestResult result= createTestResult();
+		result.addListener(fPrinter);
+		long startTime= System.currentTimeMillis();
+		suite.run(result);
+		long endTime= System.currentTimeMillis();
+		long runTime= endTime-startTime;
+		fPrinter.print(result, runTime);
+
+		pause(wait);
+		return result;
+	}
+
+	protected void pause(boolean wait) {
+		if (!wait) return;
+		fPrinter.printWaitPrompt();
+		try {
+			System.in.read();
+		}
+		catch(Exception e) {
+		}
+	}
+	
+	public static void main(String args[]) {
+		TestRunner aTestRunner= new TestRunner();
+		try {
+			TestResult r= aTestRunner.start(args);
+			if (!r.wasSuccessful()) 
+				System.exit(FAILURE_EXIT);
+			System.exit(SUCCESS_EXIT);
+		} catch(Exception e) {
+			System.err.println(e.getMessage());
+			System.exit(EXCEPTION_EXIT);
+		}
+	}
+
+	/**
+	 * Starts a test run. Analyzes the command line arguments
+	 * and runs the given test suite.
+	 */
+	protected TestResult start(String args[]) throws Exception {
+		String testCase= "";
+		boolean wait= false;
+		
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-wait"))
+				wait= true;
+			else if (args[i].equals("-c")) 
+				testCase= extractClassName(args[++i]);
+			else if (args[i].equals("-v"))
+				System.err.println("JUnit "+Version.id()+" by Kent Beck and Erich Gamma");
+			else
+				testCase= args[i];
+		}
+		
+		if (testCase.equals("")) 
+			throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+
+		try {
+			Test suite= getTest(testCase);
+			return doRun(suite, wait);
+		}
+		catch(Exception e) {
+			throw new Exception("Could not create and run test suite: "+e);
+		}
+	}
+		
+	protected void runFailed(String message) {
+		System.err.println(message);
+		System.exit(FAILURE_EXIT);
+	}
+	
+	public void setPrinter(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+		
+	
+}
diff --git a/dx/tests/001-nop/expected.txt b/dx/tests/001-nop/expected.txt
new file mode 100644
index 0000000..d4a85ce
--- /dev/null
+++ b/dx/tests/001-nop/expected.txt
@@ -0,0 +1 @@
+I am a jelly donut.
diff --git a/dx/tests/001-nop/info.txt b/dx/tests/001-nop/info.txt
new file mode 100644
index 0000000..9942f10
--- /dev/null
+++ b/dx/tests/001-nop/info.txt
@@ -0,0 +1,2 @@
+This is a sample no-op test, which does at least serve to verify that the
+test harness is working.
diff --git a/dx/tests/001-nop/run b/dx/tests/001-nop/run
new file mode 100644
index 0000000..51637c1
--- /dev/null
+++ b/dx/tests/001-nop/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo 'I am a jelly donut.'
diff --git a/dx/tests/002-minimal-valid/expected.txt b/dx/tests/002-minimal-valid/expected.txt
new file mode 100644
index 0000000..3877fb5
--- /dev/null
+++ b/dx/tests/002-minimal-valid/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+  0001: method{java.lang.Object.<init>:()V}
+  0002: type{Small}
+  0003: type{java.lang.Object}
+  0004: utf8{"<init>"}
+  0005: utf8{"()V"}
+  0006: utf8{"Code"}
+  0007: nat{<init>:()V}
+  0008: utf8{"Small"}
+  0009: utf8{"java/lang/Object"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: <init>
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000011
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000005
+    0000: aload_0 // 00
+    0001: invokespecial method{java.lang.Object.<init>:()V}
+    0004: return
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/002-minimal-valid/info.txt b/dx/tests/002-minimal-valid/info.txt
new file mode 100644
index 0000000..f296af8
--- /dev/null
+++ b/dx/tests/002-minimal-valid/info.txt
@@ -0,0 +1 @@
+This is just a dump of a simple but valid class.
diff --git a/dx/tests/002-minimal-valid/run b/dx/tests/002-minimal-valid/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/002-minimal-valid/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/002-minimal-valid/small-class.txt b/dx/tests/002-minimal-valid/small-class.txt
new file mode 100644
index 0000000..25a323f
--- /dev/null
+++ b/dx/tests/002-minimal-valid/small-class.txt
@@ -0,0 +1,49 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000a       # constant_pool_count
+
+#
+# constant_pool
+#
+0a 0003 0007               # 0001: method[0003, 0007]
+07 0008                    # 0002: class[0008]
+07 0009                    # 0003: class[0009]
+01 0006 "<init>"           # 0004: utf8["<init>"]
+01 0003 "()V"              # 0005: utf8["()V"]
+01 0004 "Code"             # 0006: utf8["Code"]
+0c 0004 0005               # 0007: nat[0004, 0005]
+01 0005 "Small"            # 0008: utf8["Small"]
+01 0010 "java/lang/Object" # 0009: utf8["java/lang/Object"]
+
+0021  # access_flags
+0002  # this_class
+0003  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+#
+# methods[0]
+#
+0001  # access_flags
+0004  # name
+0005  # descriptor
+0001  # attributes_count
+# attributes[0]
+0006       # name
+0000 0011  # length
+0001       # max_stack
+0001       # max_locals
+0000 0005  # code_length
+2a         # 0000: aload_0
+b7 0001    # 0001: invokespecial method[java/lang/Object.<init>:()V]
+b1         # 0004: return
+0000       # exception_table_length
+0000       # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-bad-magic.txt b/dx/tests/003-magic-version-access/class-bad-magic.txt
new file mode 100644
index 0000000..f3c64bd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-bad-magic.txt
@@ -0,0 +1,25 @@
+#
+# classfile with a bad magic value
+#
+
+dead babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.0.txt b/dx/tests/003-magic-version-access/class-version-44.0.txt
new file mode 100644
index 0000000..2d9055c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe  # magic
+0000       # minor_version
+002c       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.65535.txt b/dx/tests/003-magic-version-access/class-version-44.65535.txt
new file mode 100644
index 0000000..0f2b582
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe  # magic
+ffff       # minor_version
+002c       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.0.txt b/dx/tests/003-magic-version-access/class-version-45.0.txt
new file mode 100644
index 0000000..335079d
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the lowest valid version, 45.0 (0x2d.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+002d       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.65535.txt b/dx/tests/003-magic-version-access/class-version-45.65535.txt
new file mode 100644
index 0000000..2b31404
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 45.65535 (0x2d.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+002d       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.0.txt b/dx/tests/003-magic-version-access/class-version-48.0.txt
new file mode 100644
index 0000000..551b221
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.0 (0x30.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0030       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.65535.txt b/dx/tests/003-magic-version-access/class-version-48.65535.txt
new file mode 100644
index 0000000..ac95b52
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.65535 (0x30.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0030       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.0.txt b/dx/tests/003-magic-version-access/class-version-49.0.txt
new file mode 100644
index 0000000..0b30fcd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the highest valid version, 49.0 (0x31.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.1.txt b/dx/tests/003-magic-version-access/class-version-49.1.txt
new file mode 100644
index 0000000..9eb477c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with a minor version 1 higher than the highest valid
+# version.  49.1 (0x31.0x01)
+#
+
+cafe babe  # magic
+0001       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.65535.txt b/dx/tests/003-magic-version-access/class-version-49.65535.txt
new file mode 100644
index 0000000..668631b
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with the same major version
+# as the highest valid version.  49.65535 (0x31.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.0.txt b/dx/tests/003-magic-version-access/class-version-50.0.txt
new file mode 100644
index 0000000..fa67077
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.1.txt b/dx/tests/003-magic-version-access/class-version-50.1.txt
new file mode 100644
index 0000000..9543be1
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0001       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.65535.txt b/dx/tests/003-magic-version-access/class-version-50.65535.txt
new file mode 100644
index 0000000..9db1958
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-51.0.txt b/dx/tests/003-magic-version-access/class-version-51.0.txt
new file mode 100644
index 0000000..2ffb4cd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-51.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0033       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/expected.txt b/dx/tests/003-magic-version-access/expected.txt
new file mode 100644
index 0000000..a632922
--- /dev/null
+++ b/dx/tests/003-magic-version-access/expected.txt
@@ -0,0 +1,243 @@
+reading class-bad-magic.txt...
+begin classfile
+magic: deadbabe
+minor_version: 0000
+major_version: 0031
+
+trouble parsing:
+bad class file magic (deadbabe) or version (0031.0000)
+...while parsing class-bad-magic.txt
+reading class-version-44.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.0000)
+...while parsing class-version-44.0.txt
+reading class-version-44.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.ffff)
+...while parsing class-version-44.65535.txt
+reading class-version-45.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-45.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0032
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.0001)
+...while parsing class-version-50.1.txt
+reading class-version-50.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.ffff)
+...while parsing class-version-50.65535.txt
+reading class-version-51.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0033
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0033.0000)
+...while parsing class-version-51.0.txt
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/003-magic-version-access/info.txt b/dx/tests/003-magic-version-access/info.txt
new file mode 100644
index 0000000..4d6a697
--- /dev/null
+++ b/dx/tests/003-magic-version-access/info.txt
@@ -0,0 +1,9 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bits of parsing tested here are:
+
+* magic number
+* major / minor version numbers
+* class access_flags
diff --git a/dx/tests/003-magic-version-access/run b/dx/tests/003-magic-version-access/run
new file mode 100644
index 0000000..24de48e
--- /dev/null
+++ b/dx/tests/003-magic-version-access/run
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The tests that don't specify "--debug" are expected to
+# throw exceptions.  If --debug is on we'll get a stack
+# trace, which is unpredictable and doesn't work well with
+# expected.txt vs. out.txt comparisons.
+
+# Bad magic (throws an expection)
+dx         --dump --strict class-bad-magic.txt
+
+# Too small (throws an exception)
+dx         --dump --strict class-version-44.0.txt
+dx         --dump --strict class-version-44.65535.txt
+
+# Just right
+dx --debug --dump --width=100 class-version-45.0.txt
+dx --debug --dump --width=100 class-version-45.65535.txt
+dx --debug --dump --width=100 class-version-48.0.txt
+dx --debug --dump --width=100 class-version-48.65535.txt
+dx --debug --dump --width=100 class-version-49.0.txt
+dx --debug --dump --width=100 class-version-49.1.txt
+dx --debug --dump --width=100 class-version-49.65535.txt
+dx --debug --dump --width=100 class-version-50.0.txt
+
+# Too big (throws an exception)
+dx         --dump --strict class-version-50.1.txt
+dx         --dump --strict class-version-50.65535.txt
+dx         --dump --strict class-version-51.0.txt
+
+# Show that we can dump the access flags even when they
+# don't make any sense.
+dx --debug --dump --width=100 small-class.txt
diff --git a/dx/tests/003-magic-version-access/small-class.txt b/dx/tests/003-magic-version-access/small-class.txt
new file mode 100644
index 0000000..3eb7402
--- /dev/null
+++ b/dx/tests/003-magic-version-access/small-class.txt
@@ -0,0 +1,25 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/004-cp-bottom-up/expected.txt b/dx/tests/004-cp-bottom-up/expected.txt
new file mode 100644
index 0000000..4edbed5
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"blort"}
+  0006: utf8{"x/y/Zzz"}
+  0007: utf8{"()V"}
+  0008: nat{blort:x/y/Zzz}
+  0009: nat{blort:()V}
+  000a: field{Small.blort:x/y/Zzz}
+  000b: method{Small.blort:()V}
+  000c: ifaceMethod{Small.blort:()V}
+  000d: string{"Small"}
+  000e: int{0x12345678 / 305419896}
+  000f: float{0x42f6e666 / 123.45}
+  0010: long{0x123456789abcdef0 / 1311768467463790320}
+  0012: double{0x411958955f8a0903 / 415269.3433}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/004-cp-bottom-up/info.txt b/dx/tests/004-cp-bottom-up/info.txt
new file mode 100644
index 0000000..f78a626
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur before the
+referring entries.
diff --git a/dx/tests/004-cp-bottom-up/run b/dx/tests/004-cp-bottom-up/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/004-cp-bottom-up/small-class.txt b/dx/tests/004-cp-bottom-up/small-class.txt
new file mode 100644
index 0000000..8a68cbf
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0014       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0007 "x/y/Zzz"          # 0006: utf8["x/y/Zzz"]
+01 0003 "()V"              # 0007: utf8["()V"]
+0c 0005 0006               # 0008: nat[blort:x/y/Zzz]
+0c 0005 0007               # 0009: nat[blort:()V]
+09 0003 0008               # 000a: field[Small.blort:x/y/Zzz]
+0a 0003 0009               # 000b: method[Small.blort:()V]
+0b 0003 0009               # 000c: ifaceMethod[Small.blort:()V]
+08 0001                    # 000d: string["Small"]
+03 12345678                # 000e: integer[0x12345678]
+04 42f6e666                # 000f: float[123.45]
+05 12345678 9abcdef0       # 0010: long[0x1234567890abcdef0]
+06 41195895 5f8a0903       # 0012: double[415269.3433]
+
+0001  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/005-cp-top-down/expected.txt b/dx/tests/005-cp-top-down/expected.txt
new file mode 100644
index 0000000..791b9da
--- /dev/null
+++ b/dx/tests/005-cp-top-down/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+  0001: double{0x411958955f8a0903 / 415269.3433}
+  0003: long{0x123456789abcdef0 / 1311768467463790320}
+  0005: float{0x42f6e666 / 123.45}
+  0006: int{0x12345678 / 305419896}
+  0007: string{"Small"}
+  0008: ifaceMethod{Small.blort:()V}
+  0009: method{Small.blort:()V}
+  000a: field{Small.blort:x/y/Zzz}
+  000b: nat{blort:()V}
+  000c: nat{blort:x/y/Zzz}
+  000d: utf8{"()V"}
+  000e: utf8{"x/y/Zzz"}
+  000f: utf8{"blort"}
+  0010: type{java.lang.Object}
+  0011: type{Small}
+  0012: utf8{"java/lang/Object"}
+  0013: utf8{"Small"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/005-cp-top-down/info.txt b/dx/tests/005-cp-top-down/info.txt
new file mode 100644
index 0000000..5842fb3
--- /dev/null
+++ b/dx/tests/005-cp-top-down/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur after the
+referring entries.
diff --git a/dx/tests/005-cp-top-down/run b/dx/tests/005-cp-top-down/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/005-cp-top-down/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/005-cp-top-down/small-class.txt b/dx/tests/005-cp-top-down/small-class.txt
new file mode 100644
index 0000000..a681f65
--- /dev/null
+++ b/dx/tests/005-cp-top-down/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0014       # constant_pool_count
+
+#
+# constant_pool
+#
+06 41195895 5f8a0903       # 0001: double[415269.3433]
+05 12345678 9abcdef0       # 0003: long[0x1234567890abcdef0]
+04 42f6e666                # 0005: float[123.45]
+03 12345678                # 0006: integer[0x12345678]
+08 0013                    # 0007: string["Small"]
+0b 0011 000b               # 0008: ifaceMethod[Small.blort:()V]
+0a 0011 000b               # 0009: method[Small.blort:()V]
+09 0011 000c               # 000a: field[Small.blort:x/y/Zzz]
+0c 000f 000d               # 000b: nat[blort:()V]
+0c 000f 000e               # 000c: nat[blort:x/y/Zzz]
+01 0003 "()V"              # 000d: utf8["()V"]
+01 0007 "x/y/Zzz"          # 000e: utf8["x/y/Zzz"]
+01 0005 "blort"            # 000f: utf8["blort"]
+07 0012                    # 0010: class[java/lang/Object]
+07 0013                    # 0011: class[Small]
+01 0010 "java/lang/Object" # 0012: utf8["java/lang/Object"]
+01 0005 "Small"            # 0013: utf8["Small"]
+
+0001  # access_flags
+0011  # this_class
+0010  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/006-interfaces/expected.txt b/dx/tests/006-interfaces/expected.txt
new file mode 100644
index 0000000..09e066b
--- /dev/null
+++ b/dx/tests/006-interfaces/expected.txt
@@ -0,0 +1,31 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000b
+
+constant_pool:
+  0001: utf8{"java/lang/Object"}
+  0002: utf8{"Small"}
+  0003: utf8{"Foo"}
+  0004: utf8{"Bar"}
+  0005: utf8{"Baz"}
+  0006: type{java.lang.Object}
+  0007: type{Small}
+  0008: type{Foo}
+  0009: type{Bar}
+  000a: type{Baz}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0003
+interfaces:
+  type{Foo}
+  type{Bar}
+  type{Baz}
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/006-interfaces/info.txt b/dx/tests/006-interfaces/info.txt
new file mode 100644
index 0000000..0959482
--- /dev/null
+++ b/dx/tests/006-interfaces/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a non-empty
+interfaces list.
diff --git a/dx/tests/006-interfaces/run b/dx/tests/006-interfaces/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/006-interfaces/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/006-interfaces/small-class.txt b/dx/tests/006-interfaces/small-class.txt
new file mode 100644
index 0000000..ea24923
--- /dev/null
+++ b/dx/tests/006-interfaces/small-class.txt
@@ -0,0 +1,33 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000b       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+01 0005 "Small"            # 0002: utf8["Small"]
+01 0003 "Foo"              # 0003: utf8["Foo"]
+01 0003 "Bar"              # 0004: utf8["Bar"]
+01 0003 "Baz"              # 0005: utf8["Baz"]
+07 0001                    # 0006: class[java/lang/Object]
+07 0002                    # 0007: class[Small]
+07 0003                    # 0008: class[Foo]
+07 0004                    # 0009: class[Bar]
+07 0005                    # 000a: class[Baz]
+
+0001  # access_flags
+0007  # this_class
+0006  # super_class
+0003  # interfaces_count
+0008 0009 000a  # interfaces
+
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/007-no-superclass/expected.txt b/dx/tests/007-no-superclass/expected.txt
new file mode 100644
index 0000000..d635c9a
--- /dev/null
+++ b/dx/tests/007-no-superclass/expected.txt
@@ -0,0 +1,19 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0003
+
+constant_pool:
+  0001: utf8{"java/lang/Object"}
+  0002: type{java.lang.Object}
+end constant_pool
+access_flags: public
+this_class: type{java.lang.Object}
+super_class: (none)
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/007-no-superclass/info.txt b/dx/tests/007-no-superclass/info.txt
new file mode 100644
index 0000000..5f941d0
--- /dev/null
+++ b/dx/tests/007-no-superclass/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class is Object-like and
+has no superclass.
diff --git a/dx/tests/007-no-superclass/run b/dx/tests/007-no-superclass/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/007-no-superclass/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/007-no-superclass/small-class.txt b/dx/tests/007-no-superclass/small-class.txt
new file mode 100644
index 0000000..6fd4408
--- /dev/null
+++ b/dx/tests/007-no-superclass/small-class.txt
@@ -0,0 +1,24 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0003       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+07 0001                    # 0002: class[java/lang/Object]
+
+0001  # access_flags
+0002  # this_class
+0000  # super_class
+0000  # interfaces_count
+
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/008-field/expected.txt b/dx/tests/008-field/expected.txt
new file mode 100644
index 0000000..9e3bcaf
--- /dev/null
+++ b/dx/tests/008-field/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"foo"}
+  0006: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public|private|protected|static|final|volatile|transient|synthetic|enum|af20
+  name: foo
+  descriptor: I
+  attributes_count: 0000
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/008-field/info.txt b/dx/tests/008-field/info.txt
new file mode 100644
index 0000000..0b8e92f
--- /dev/null
+++ b/dx/tests/008-field/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple field with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/008-field/run b/dx/tests/008-field/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/008-field/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/008-field/small-class.txt b/dx/tests/008-field/small-class.txt
new file mode 100644
index 0000000..81eb164
--- /dev/null
+++ b/dx/tests/008-field/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0003 "foo"              # 0005: utf8["foo"]
+01 0001 "I"                # 0006: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+ffff  # access_flags
+0005  # name
+0006  # descriptor
+0000  # attributes_count
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/009-method/expected.txt b/dx/tests/009-method/expected.txt
new file mode 100644
index 0000000..3c0d6ad
--- /dev/null
+++ b/dx/tests/009-method/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"foo"}
+  0006: utf8{"()V"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|private|protected|static|final|synchronized|bridge|varargs|native|abstract|strictfp|synthetic|e200
+  name: foo
+  descriptor: ()V
+  attributes_count: 0000
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/009-method/info.txt b/dx/tests/009-method/info.txt
new file mode 100644
index 0000000..3df2f09
--- /dev/null
+++ b/dx/tests/009-method/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple method with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/009-method/run b/dx/tests/009-method/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/009-method/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/009-method/small-class.txt b/dx/tests/009-method/small-class.txt
new file mode 100644
index 0000000..54fbc5f
--- /dev/null
+++ b/dx/tests/009-method/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0003 "foo"              # 0005: utf8["foo"]
+01 0003 "()V"              # 0006: utf8["()V"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+
+0001  # methods_count
+
+# methods[0]
+ffff  # access_flags
+0005  # name
+0006  # descriptor
+0000  # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/010-class-attrib-InnerClasses/expected.txt b/dx/tests/010-class-attrib-InnerClasses/expected.txt
new file mode 100644
index 0000000..590ed2e
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"InnerClasses"}
+  0006: utf8{"Zorch"}
+  0007: type{Zorch}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: InnerClasses
+  length: 00000022
+  number_of_classes: 0004
+  inner_class: type{Small}
+    outer_class: (none)
+    name: (none)
+    access_flags: public
+  inner_class: type{Small}
+    outer_class: (none)
+    name: utf8{"Small"}
+    access_flags: private
+  inner_class: type{Small}
+    outer_class: type{Zorch}
+    name: (none)
+    access_flags: protected
+  inner_class: type{Zorch}
+    outer_class: type{Small}
+    name: utf8{"Zorch"}
+    access_flags: public|private|protected|static|final|interface|abstract|synthetic|annotation|enum|89e0
+end attributes[0]
+end classfile
diff --git a/dx/tests/010-class-attrib-InnerClasses/info.txt b/dx/tests/010-class-attrib-InnerClasses/info.txt
new file mode 100644
index 0000000..305b035
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level InnerClasses attribute, which is syntactically valid and contains
+one entry for each of the possible combinations of null-vs-valid cpe.
diff --git a/dx/tests/010-class-attrib-InnerClasses/run b/dx/tests/010-class-attrib-InnerClasses/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/010-class-attrib-InnerClasses/small-class.txt b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
new file mode 100644
index 0000000..6ad13a4
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000c "InnerClasses"     # 0005: utf8["InnerClasses"]
+01 0005 "Zorch"            # 0006: utf8["Zorch"]
+07 0006                    # 0007: class[Zorch]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000022  # length
+0004      # number_of_classes
+0003 0000 0000 0001  # Small / null / null / public
+0003 0000 0001 0002  # Small / null / "Small" / private
+0003 0007 0000 0004  # Small / Zorch / null / protected
+0007 0003 0006 ffff  # Zorch / Small / "Zorch" / all-bits
+
diff --git a/dx/tests/011-class-attrib-Synthetic/expected.txt b/dx/tests/011-class-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..85e2bff
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Synthetic"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Synthetic
+  length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/011-class-attrib-Synthetic/info.txt b/dx/tests/011-class-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..bfd443e
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Synthetic attribute, which is syntactically valid.
diff --git a/dx/tests/011-class-attrib-Synthetic/run b/dx/tests/011-class-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/011-class-attrib-Synthetic/small-class.txt b/dx/tests/011-class-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..bc3281b
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Synthetic"        # 0005: utf8["Synthetic"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000000  # length
diff --git a/dx/tests/012-class-attrib-SourceFile/expected.txt b/dx/tests/012-class-attrib-SourceFile/expected.txt
new file mode 100644
index 0000000..c795cde
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"SourceFile"}
+  0006: utf8{"Blort.java"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: SourceFile
+  length: 00000002
+  source: utf8{"Blort.java"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/012-class-attrib-SourceFile/info.txt b/dx/tests/012-class-attrib-SourceFile/info.txt
new file mode 100644
index 0000000..f1d8985
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level SourceFile attribute, which is syntactically valid.
diff --git a/dx/tests/012-class-attrib-SourceFile/run b/dx/tests/012-class-attrib-SourceFile/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/012-class-attrib-SourceFile/small-class.txt b/dx/tests/012-class-attrib-SourceFile/small-class.txt
new file mode 100644
index 0000000..3c514be
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "SourceFile"       # 0005: utf8["SourceFile"]
+01 000a "Blort.java"       # 0006: utf8["Blort.java"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000002  # length
+0006      # "Blort.java"
diff --git a/dx/tests/013-class-attrib-Deprecated/expected.txt b/dx/tests/013-class-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..4476c89
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Deprecated"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Deprecated
+  length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/013-class-attrib-Deprecated/info.txt b/dx/tests/013-class-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..8164fe1
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Deprecated attribute, which is syntactically valid.
diff --git a/dx/tests/013-class-attrib-Deprecated/run b/dx/tests/013-class-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/013-class-attrib-Deprecated/small-class.txt b/dx/tests/013-class-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..03d7e24
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "Deprecated"       # 0005: utf8["Deprecated"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000000  # length
diff --git a/dx/tests/014-field-attrib-ConstantValue/expected.txt b/dx/tests/014-field-attrib-ConstantValue/expected.txt
new file mode 100644
index 0000000..b3d91a5
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/expected.txt
@@ -0,0 +1,162 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 001f
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"ConstantValue"}
+  0006: utf8{"a"}
+  0007: utf8{"b"}
+  0008: utf8{"c"}
+  0009: utf8{"d"}
+  000a: utf8{"e"}
+  000b: utf8{"f"}
+  000c: utf8{"g"}
+  000d: utf8{"h"}
+  000e: utf8{"i"}
+  000f: string{"Small"}
+  0010: int{0x8191a1b1 / -2121162319}
+  0011: float{0xbffeb852 / -1.99}
+  0012: long{0x80818283f0f1f2f3 / -9186918261664386317}
+  0014: double{0xbfffd70a3d70a3d7 / -1.99}
+  0016: utf8{"B"}
+  0017: utf8{"C"}
+  0018: utf8{"D"}
+  0019: utf8{"F"}
+  001a: utf8{"I"}
+  001b: utf8{"J"}
+  001c: utf8{"S"}
+  001d: utf8{"Z"}
+  001e: utf8{"Ljava/lang/String;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0009
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: B
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[0]
+
+fields[1]:
+  access_flags: private
+  name: b
+  descriptor: C
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[1]
+
+fields[2]:
+  access_flags: protected
+  name: c
+  descriptor: D
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: double{0xbfffd70a3d70a3d7 / -1.99}
+  end attributes[0]
+end fields[2]
+
+fields[3]:
+  access_flags: static
+  name: d
+  descriptor: F
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: float{0xbffeb852 / -1.99}
+  end attributes[0]
+end fields[3]
+
+fields[4]:
+  access_flags: final
+  name: e
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[4]
+
+fields[5]:
+  access_flags: volatile
+  name: f
+  descriptor: J
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: long{0x80818283f0f1f2f3 / -9186918261664386317}
+  end attributes[0]
+end fields[5]
+
+fields[6]:
+  access_flags: transient
+  name: g
+  descriptor: S
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[6]
+
+fields[7]:
+  access_flags: public|static|final
+  name: h
+  descriptor: Z
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[7]
+
+fields[8]:
+  access_flags: public|static|final
+  name: i
+  descriptor: Ljava/lang/String;
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: string{"Small"}
+  end attributes[0]
+end fields[8]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/014-field-attrib-ConstantValue/info.txt b/dx/tests/014-field-attrib-ConstantValue/info.txt
new file mode 100644
index 0000000..313159c
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a series of
+fields, each with a single ConstantValue attribute, which points at one
+of the appropriate sorts of cpes.
diff --git a/dx/tests/014-field-attrib-ConstantValue/run b/dx/tests/014-field-attrib-ConstantValue/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/014-field-attrib-ConstantValue/small-class.txt b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
new file mode 100644
index 0000000..8d869de
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
@@ -0,0 +1,140 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+001f       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"              # 0001: utf8["Small"]
+01 0010 "java/lang/Object"   # 0002: utf8["java/lang/Object"]
+07 0001                      # 0003: class[Small]
+07 0002                      # 0004: class[java/lang/Object]
+01 000d "ConstantValue"      # 0005: utf8["ConstantValue"]
+01 0001 "a"                  # 0006: utf8["a"]
+01 0001 "b"                  # 0007: utf8["b"]
+01 0001 "c"                  # 0008: utf8["c"]
+01 0001 "d"                  # 0009: utf8["d"]
+01 0001 "e"                  # 000a: utf8["e"]
+01 0001 "f"                  # 000b: utf8["f"]
+01 0001 "g"                  # 000c: utf8["g"]
+01 0001 "h"                  # 000d: utf8["h"]
+01 0001 "i"                  # 000e: utf8["i"]
+08 0001                      # 000f: string["Small"]
+03 8191a1b1                  # 0010: integer[0x8191a1b1]
+04 bffeb852                  # 0011: float[-1.99]
+05 80818283 f0f1f2f3         # 0012: long[0x80818283f0f1f2f3]
+06 bfffd70a 3d70a3d7         # 0014: double[-1.99]
+01 0001 "B"                  # 0016: utf8["B"]
+01 0001 "C"                  # 0017: utf8["C"]
+01 0001 "D"                  # 0018: utf8["D"]
+01 0001 "F"                  # 0019: utf8["F"]
+01 0001 "I"                  # 001a: utf8["I"]
+01 0001 "J"                  # 001b: utf8["J"]
+01 0001 "S"                  # 001c: utf8["S"]
+01 0001 "Z"                  # 001d: utf8["Z"]
+01 0012 "Ljava/lang/String;" # 001e: utf8["Ljava/lang/String;"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0009  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0016  # "B"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[1]
+0002  # access_flags
+0007  # "b"
+0017  # "C"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[2]
+0004  # access_flags
+0008  # "c"
+0018  # "D"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0014      # value
+
+# fields[3]
+0008  # access_flags
+0009  # "d"
+0019  # "F"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0011      # value
+
+# fields[4]
+0010  # access_flags
+000a  # "e"
+001a  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[5]
+0040  # access_flags
+000b  # "f"
+001b  # "J"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0012      # value
+
+# fields[6]
+0080  # access_flags
+000c  # "g"
+001c  # "Z"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[7]
+0019  # access_flags
+000d  # "h"
+001d  # "S"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[8]
+0019  # access_flags
+000e  # "i"
+001e  # "Ljava/lang/String;"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+000f      # value
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/015-field-attrib-Synthetic/expected.txt b/dx/tests/015-field-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..bdb4519
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Synthetic"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Synthetic
+    length: 00000000
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/015-field-attrib-Synthetic/info.txt b/dx/tests/015-field-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..6037cfa
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Synthetic attribute.
diff --git a/dx/tests/015-field-attrib-Synthetic/run b/dx/tests/015-field-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/015-field-attrib-Synthetic/small-class.txt b/dx/tests/015-field-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..e5f429e
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Synthetic"        # 0005: utf8["Synthetic"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000000  # length
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/016-field-attrib-Deprecated/expected.txt b/dx/tests/016-field-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..1b5547f
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Deprecated"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Deprecated
+    length: 00000000
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/016-field-attrib-Deprecated/info.txt b/dx/tests/016-field-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..1185981
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Deprecated attribute.
diff --git a/dx/tests/016-field-attrib-Deprecated/run b/dx/tests/016-field-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/016-field-attrib-Deprecated/small-class.txt b/dx/tests/016-field-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..2448ce4
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "Deprecated"       # 0005: utf8["Deprecated"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000000  # length
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/017-method-attrib-Code/expected.txt b/dx/tests/017-method-attrib-Code/expected.txt
new file mode 100644
index 0000000..c43730c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/expected.txt
@@ -0,0 +1,42 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 0000000d
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000001
+    0000: return
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/017-method-attrib-Code/info.txt b/dx/tests/017-method-attrib-Code/info.txt
new file mode 100644
index 0000000..a3ed24c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute.
diff --git a/dx/tests/017-method-attrib-Code/run b/dx/tests/017-method-attrib-Code/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/017-method-attrib-Code/small-class.txt b/dx/tests/017-method-attrib-Code/small-class.txt
new file mode 100644
index 0000000..699005b
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/small-class.txt
@@ -0,0 +1,43 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+0000000d  # length
+0001      # max_stack
+0001      # max_locals
+00000001  # code_length
+b1        # 0000: return
+0000      # exception_table_length
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/018-method-attrib-Exceptions/expected.txt b/dx/tests/018-method-attrib-Exceptions/expected.txt
new file mode 100644
index 0000000..15f923f
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/expected.txt
@@ -0,0 +1,40 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Exceptions"}
+  0008: utf8{"java/lang/Error"}
+  0009: type{java.lang.Error}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Exceptions
+    length: 00000004
+    number_of_exceptions: 0001
+      type{java.lang.Error}
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/018-method-attrib-Exceptions/info.txt b/dx/tests/018-method-attrib-Exceptions/info.txt
new file mode 100644
index 0000000..4a3738d
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Exceptions attribute.
diff --git a/dx/tests/018-method-attrib-Exceptions/run b/dx/tests/018-method-attrib-Exceptions/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/018-method-attrib-Exceptions/small-class.txt b/dx/tests/018-method-attrib-Exceptions/small-class.txt
new file mode 100644
index 0000000..5d05b43
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/small-class.txt
@@ -0,0 +1,41 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000a       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 000a "Exceptions"       # 0007: utf8["Exceptions"]
+01 000f "java/lang/Error"  # 0008: utf8["java/lang/Error"]
+07 0008                    # 0009: class[java/lang/Error]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000004  # length
+0001      # number_of_exceptions
+0009      # class[java/lang/Error]
+
+0000  # attributes_count
diff --git a/dx/tests/019-method-attrib-Synthetic/expected.txt b/dx/tests/019-method-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..5d1d9cb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Synthetic"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Synthetic
+    length: 00000000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/019-method-attrib-Synthetic/info.txt b/dx/tests/019-method-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..eddf4fb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Synthetic attribute.
diff --git a/dx/tests/019-method-attrib-Synthetic/run b/dx/tests/019-method-attrib-Synthetic/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/019-method-attrib-Synthetic/small-class.txt b/dx/tests/019-method-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..458dae8
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0009 "Synthetic"        # 0007: utf8["Synthetic"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000000  # length
+
+0000  # attributes_count
diff --git a/dx/tests/020-method-attrib-Deprecated/expected.txt b/dx/tests/020-method-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..8da8aa8
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Deprecated"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Deprecated
+    length: 00000000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/020-method-attrib-Deprecated/info.txt b/dx/tests/020-method-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..b7c6266
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Deprecated attribute.
diff --git a/dx/tests/020-method-attrib-Deprecated/run b/dx/tests/020-method-attrib-Deprecated/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/020-method-attrib-Deprecated/small-class.txt b/dx/tests/020-method-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..f906733
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 000a "Deprecated"       # 0007: utf8["Deprecated"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000000  # length
+
+0000  # attributes_count
diff --git a/dx/tests/021-code-attrib-LineNumberTable/expected.txt b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
new file mode 100644
index 0000000..3f29310
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
@@ -0,0 +1,52 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"LineNumberTable"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 0000001e
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000002
+    0000: return
+    0001: return
+    exception_table_length: 0000
+    attributes_count: 0001
+    
+    attributes[0]:
+      name: LineNumberTable
+      length: 0000000a
+      line_number_table_length: 0002
+      0000 17
+      0001 34
+    end attributes[0]
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/021-code-attrib-LineNumberTable/info.txt b/dx/tests/021-code-attrib-LineNumberTable/info.txt
new file mode 100644
index 0000000..3e81d29
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LineNumberTable attribute.
+
diff --git a/dx/tests/021-code-attrib-LineNumberTable/run b/dx/tests/021-code-attrib-LineNumberTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/021-code-attrib-LineNumberTable/small-class.txt b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
new file mode 100644
index 0000000..c28c39a
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
@@ -0,0 +1,51 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+01 000f "LineNumberTable"  # 0008: utf8["LineNumberTable"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+0000001e  # length
+0001      # max_stack
+0001      # max_locals
+00000002  # code_length
+b1        # 0000: return
+b1        # 0001: return
+0000      # exception_table_length
+0001      # attributes_count
+# attributes[0]
+0008      # name
+0000000a  # length
+0002      # line_number_table_length
+0000 0011 # offset 0000, line #17
+0001 0022 # offset 0001, line #34
+
+0000  # attributes_count
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/expected.txt b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
new file mode 100644
index 0000000..80091ed
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
@@ -0,0 +1,57 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000d
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"LocalVariableTable"}
+  0009: utf8{"foo"}
+  000a: utf8{"bar"}
+  000b: utf8{"baz"}
+  000c: utf8{"[I"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000034
+    max_stack: 0001
+    max_locals: 0002
+    code_length: 00000002
+    0000: return
+    0001: return
+    exception_table_length: 0000
+    attributes_count: 0001
+    
+    attributes[0]:
+      name: LocalVariableTable
+      length: 00000020
+      local_variable_table_length: 0003
+      0000..0002 0000 foo [I
+      0000..0001 0001 bar [I
+      0001..0002 0001 baz [I
+    end attributes[0]
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/info.txt b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
new file mode 100644
index 0000000..d1afa33
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LocalVariableTable attribute.
+
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/run b/dx/tests/022-code-attrib-LocalVariableTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
new file mode 100644
index 0000000..abbf8ea
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
@@ -0,0 +1,56 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000d       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                      # 0001: class[Small]
+07 0004                      # 0002: class[java/lang/Object]
+01 0005 "Small"              # 0003: utf8["Small"]
+01 0010 "java/lang/Object"   # 0004: utf8["java/lang/Object"]
+01 0005 "blort"              # 0005: utf8["blort"]
+01 0003 "()V"                # 0006: utf8["()V"]
+01 0004 "Code"               # 0007: utf8["Code"]
+01 0012 "LocalVariableTable" # 0008: utf8["LocalVariableTable"]
+01 0003 "foo"                # 0009: utf8["foo"]
+01 0003 "bar"                # 000a: utf8["bar"]
+01 0003 "baz"                # 000b: utf8["baz"]
+01 0002 "[I"                 # 000c: utf8["[I"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000034  # length
+0001      # max_stack
+0002      # max_locals
+00000002  # code_length
+b1        # 0000: return
+b1        # 0001: return
+0000      # exception_table_length
+0001      # attributes_count
+# attributes[0]
+0008      # name
+00000020  # length
+0003      # local_variable_table_length
+0000 0002 0009 000c 0000  # 0000..0002 foo:[I #0000
+0000 0001 000a 000c 0001  # 0000..0001 bar:[I #0001
+0001 0001 000b 000c 0001  # 0001..0002 baz:[I #0001
+
+0000  # attributes_count
diff --git a/dx/tests/023-code-exception-table/expected.txt b/dx/tests/023-code-exception-table/expected.txt
new file mode 100644
index 0000000..552e5d3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/expected.txt
@@ -0,0 +1,51 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000c
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"java/lang/Error"}
+  0009: utf8{"java/lang/Exception"}
+  000a: type{java.lang.Error}
+  000b: type{java.lang.Exception}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000027
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000003
+    0000: return
+    0001: return
+    0002: return
+    exception_table_length: 0003
+      0000..0002 -> 0002 java.lang.Error
+      0000..0001 -> 0001 java.lang.Exception
+      0001..0002 -> 0002 <any>
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/023-code-exception-table/info.txt b/dx/tests/023-code-exception-table/info.txt
new file mode 100644
index 0000000..f4bb35e
--- /dev/null
+++ b/dx/tests/023-code-exception-table/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute, which
+sports a non-empty syntactically valid exception table.
+
diff --git a/dx/tests/023-code-exception-table/run b/dx/tests/023-code-exception-table/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/023-code-exception-table/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/023-code-exception-table/small-class.txt b/dx/tests/023-code-exception-table/small-class.txt
new file mode 100644
index 0000000..f8ff1f3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/small-class.txt
@@ -0,0 +1,52 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000c       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                       # 0001: class[Small]
+07 0004                       # 0002: class[java/lang/Object]
+01 0005 "Small"               # 0003: utf8["Small"]
+01 0010 "java/lang/Object"    # 0004: utf8["java/lang/Object"]
+01 0005 "blort"               # 0005: utf8["blort"]
+01 0003 "()V"                 # 0006: utf8["()V"]
+01 0004 "Code"                # 0007: utf8["Code"]
+01 000f "java/lang/Error"     # 0008: utf8["java/lang/Error"]
+01 0013 "java/lang/Exception" # 0009: utf8["java/lang/Exception"]
+07 0008                       # 000a: class[java/lang/Error]
+07 0009                       # 000b: class[java/lang/Exception]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000027  # length
+0001      # max_stack
+0001      # max_locals
+00000003  # code_length
+b1        # 0000: return
+b1        # 0001: return
+b1        # 0002: return
+0003      # exception_table_length
+0000 0002 0002 000a  # 0000..0002 -> 0002 java/lang/Error
+0000 0001 0001 000b  # 0000..0001 -> 0001 java/lang/Exception
+0001 0002 0002 0000  # 0001..0002 -> 0002 <any>
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/024-code-bytecode/expected.txt b/dx/tests/024-code-bytecode/expected.txt
new file mode 100644
index 0000000..4637474
--- /dev/null
+++ b/dx/tests/024-code-bytecode/expected.txt
@@ -0,0 +1,294 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0017
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: string{"Small"}
+  0009: int{0x12345678 / 305419896}
+  000a: float{0x42f6e666 / 123.45}
+  000b: long{0x123456789abcdef0 / 1311768467463790320}
+  000d: double{0x411958955f8a0903 / 415269.3433}
+  000f: utf8{"blort"}
+  0010: utf8{"x/y/Zzz"}
+  0011: utf8{"()V"}
+  0012: nat{blort:x/y/Zzz}
+  0013: nat{blort:()V}
+  0014: field{Small.blort:x/y/Zzz}
+  0015: method{Small.blort:()V}
+  0016: ifaceMethod{Small.blort:()V}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 000001dc
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 000001d0
+    0000: nop
+    0001: aconst_null
+    0002: iconst_m1 // #-01
+    0003: iconst_0 // #+00
+    0004: iconst_1 // #+01
+    0005: iconst_2 // #+02
+    0006: iconst_3 // #+03
+    0007: iconst_4 // #+04
+    0008: iconst_5 // #+05
+    0009: lconst_0 // +00
+    000a: lconst_1 // +01
+    000b: fconst_0 // 0.0
+    000c: fconst_1 // 1.0
+    000d: fconst_2 // 2.0
+    000e: dconst_0 // 0.0
+    000f: dconst_1 // 1.0
+    0010: bipush #+45
+    0012: sipush #+5432
+    0015: ldc string{"Small"}
+    0017: ldc #+12345678
+    0019: ldc #42f6e666 // 123.45
+    001b: ldc_w string{"Small"}
+    001e: ldc_w #+12345678
+    0021: ldc_w #42f6e666 // 123.45
+    0024: ldc2_w #+123456789abcdef0
+    0027: ldc2_w #411958955f8a0903 // 415269.3433
+    002a: iload 01
+    002c: lload 02 // category-2
+    002e: fload 03
+    0030: dload 04 // category-2
+    0032: aload 05
+    0034: iload_0 // 00
+    0035: iload_1 // 01
+    0036: iload_2 // 02
+    0037: iload_3 // 03
+    0038: lload_0 // 00, category-2
+    0039: lload_1 // 01, category-2
+    003a: lload_2 // 02, category-2
+    003b: lload_3 // 03, category-2
+    003c: fload_0 // 00
+    003d: fload_1 // 01
+    003e: fload_2 // 02
+    003f: fload_3 // 03
+    0040: dload_0 // 00, category-2
+    0041: dload_1 // 01, category-2
+    0042: dload_2 // 02, category-2
+    0043: dload_3 // 03, category-2
+    0044: aload_0 // 00
+    0045: aload_1 // 01
+    0046: aload_2 // 02
+    0047: aload_3 // 03
+    0048: iaload
+    0049: laload
+    004a: faload
+    004b: daload
+    004c: aaload
+    004d: baload
+    004e: caload
+    004f: saload
+    0050: istore 41
+    0052: lstore 42 // category-2
+    0054: fstore 43
+    0056: dstore 44 // category-2
+    0058: astore 45
+    005a: istore_0 // 00
+    005b: istore_1 // 01
+    005c: istore_2 // 02
+    005d: istore_3 // 03
+    005e: lstore_0 // 00, category-2
+    005f: lstore_1 // 01, category-2
+    0060: lstore_2 // 02, category-2
+    0061: lstore_3 // 03, category-2
+    0062: fstore_0 // 00
+    0063: fstore_1 // 01
+    0064: fstore_2 // 02
+    0065: fstore_3 // 03
+    0066: dstore_0 // 00, category-2
+    0067: dstore_1 // 01, category-2
+    0068: dstore_2 // 02, category-2
+    0069: dstore_3 // 03, category-2
+    006a: astore_0 // 00
+    006b: astore_1 // 01
+    006c: astore_2 // 02
+    006d: astore_3 // 03
+    006e: iastore
+    006f: lastore
+    0070: fastore
+    0071: dastore
+    0072: aastore
+    0073: bastore
+    0074: castore
+    0075: sastore
+    0076: pop
+    0077: pop2
+    0078: dup
+    0079: dup_x1
+    007a: dup_x2
+    007b: dup2
+    007c: dup2_x1
+    007d: dup2_x2
+    007e: swap
+    007f: iadd
+    0080: ladd
+    0081: fadd
+    0082: dadd
+    0083: isub
+    0084: lsub
+    0085: fsub
+    0086: dsub
+    0087: imul
+    0088: lmul
+    0089: fmul
+    008a: dmul
+    008b: idiv
+    008c: ldiv
+    008d: fdiv
+    008e: ddiv
+    008f: irem
+    0090: lrem
+    0091: frem
+    0092: drem
+    0093: ineg
+    0094: lneg
+    0095: fneg
+    0096: dneg
+    0097: ishl
+    0098: lshl
+    0099: ishr
+    009a: lshr
+    009b: iushr
+    009c: lushr
+    009d: iand
+    009e: land
+    009f: ior
+    00a0: lor
+    00a1: ixor
+    00a2: lxor
+    00a3: iinc 05, #-01
+    00a6: i2l
+    00a7: i2f
+    00a8: i2d
+    00a9: l2i
+    00aa: l2f
+    00ab: l2d
+    00ac: f2i
+    00ad: f2l
+    00ae: f2d
+    00af: d2i
+    00b0: d2l
+    00b1: d2f
+    00b2: i2b
+    00b3: i2c
+    00b4: i2s
+    00b5: lcmp
+    00b6: fcmpl
+    00b7: fcmpg
+    00b8: dcmpl
+    00b9: dcmpg
+    00ba: ifeq 00ba
+    00bd: ifne 00ba
+    00c0: iflt 00ba
+    00c3: ifge 00ba
+    00c6: ifgt 00ba
+    00c9: ifle 00ba
+    00cc: if_icmpeq 00db
+    00cf: if_icmpne 00db
+    00d2: if_icmplt 00db
+    00d5: if_icmpge 00db
+    00d8: if_icmpgt 00db
+    00db: if_icmple 00db
+    00de: if_acmpeq 00de
+    00e1: if_acmpne 00e1
+    00e4: goto 0000
+    00e7: jsr 00e7
+    00ea: ret 2f
+    00ec: tableswitch
+      +12340000: 0000
+      +12340001: 0001
+      +12340002: 0002
+      +12340003: 0003
+      +12340004: 0004
+      +12340005: 0005
+      +12340006: 0007
+      +12340007: 0009
+      default: 00ea
+    011c: lookupswitch
+      -7689edcc: 0148
+      +00001000: 0149
+      +03333333: 0149
+      +79787776: 014b
+      default: 00ec
+    0148: ireturn
+    0149: lreturn
+    014a: freturn
+    014b: dreturn
+    014c: areturn
+    014d: return
+    014e: getstatic field{Small.blort:x/y/Zzz}
+    0151: putstatic field{Small.blort:x/y/Zzz}
+    0154: getfield field{Small.blort:x/y/Zzz}
+    0157: putfield field{Small.blort:x/y/Zzz}
+    015a: invokevirtual method{Small.blort:()V}
+    015d: invokespecial method{Small.blort:()V}
+    0160: invokestatic method{Small.blort:()V}
+    0163: invokeinterface ifaceMethod{Small.blort:()V}, 0001
+    0168: unused_ba
+    0169: new type{Small}
+    016c: newarray boolean
+    016e: newarray char
+    0170: newarray float
+    0172: newarray double
+    0174: newarray byte
+    0176: newarray short
+    0178: newarray int
+    017a: newarray long
+    017c: anewarray type{Small}
+    017f: arraylength
+    0180: athrow
+    0181: checkcast type{java.lang.Object}
+    0184: instanceof type{java.lang.Object}
+    0187: monitorenter
+    0188: monitorexit
+    0189: wide iload 0123
+    018d: wide lload 0124 // category-2
+    0191: wide fload 0125
+    0195: wide dload 0126 // category-2
+    0199: wide aload 0127
+    019d: wide istore 20f0
+    01a1: wide lstore 20f1 // category-2
+    01a5: wide fstore 20f2
+    01a9: wide dstore 20f3 // category-2
+    01ad: wide astore 20f4
+    01b1: wide ret ffff
+    01b5: wide iinc 0002, #+1000
+    01bb: multianewarray type{java.lang.Object}, 04
+    01bf: ifnull 0000
+    01c2: ifnonnull 01c2
+    01c5: goto_w 700001c5
+    01ca: jsr_w 000001c5
+    01cf: unused_ca
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/024-code-bytecode/info.txt b/dx/tests/024-code-bytecode/info.txt
new file mode 100644
index 0000000..d1264a8
--- /dev/null
+++ b/dx/tests/024-code-bytecode/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a Code attribute, and the code[] array of the attribute has
+one instance of each interesting variant of each possible bytecode.
diff --git a/dx/tests/024-code-bytecode/run b/dx/tests/024-code-bytecode/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/024-code-bytecode/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/024-code-bytecode/small-class.txt b/dx/tests/024-code-bytecode/small-class.txt
new file mode 100644
index 0000000..2526cf2
--- /dev/null
+++ b/dx/tests/024-code-bytecode/small-class.txt
@@ -0,0 +1,304 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0017       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+08 0003                    # 0008: string["Small"]
+03 12345678                # 0009: integer[0x12345678]
+04 42f6e666                # 000a: float[123.45]
+05 12345678 9abcdef0       # 000b: long[0x1234567890abcdef0]
+06 41195895 5f8a0903       # 000d: double[415269.3433]
+01 0005 "blort"            # 000f: utf8["blort"]
+01 0007 "x/y/Zzz"          # 0010: utf8["x/y/Zzz"]
+01 0003 "()V"              # 0011: utf8["()V"]
+0c 000f 0010               # 0012: nat[blort:x/y/Zzz]
+0c 000f 0011               # 0013: nat[blort:()V]
+09 0001 0012               # 0014: field[Small.blort:x/y/Zzz]
+0a 0001 0013               # 0015: method[Small.blort:()V]
+0b 0001 0013               # 0016: ifaceMethod[Small.blort:()V]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+000001dc  # length (note: == code_length + 0x0c)
+0001      # max_stack
+0001      # max_locals
+000001d0  # code_length
+
+00        # 0000: nop
+01        # 0001: aconst_null
+02        # 0002: aconst_m1
+03        # 0003: iconst_0
+04        # 0004: iconst_1
+05        # 0005: iconst_2
+06        # 0006: iconst_3
+07        # 0007: iconst_4
+08        # 0008: iconst_5
+09        # 0009: lconst_0
+0a        # 000a: lconst_1
+0b        # 000b: fconst_0
+0c        # 000c: fconst_1
+0d        # 000d: fconst_2
+0e        # 000e: dconst_0
+0f        # 000f: dconst_1
+10 45     # 0010: bipush #+45
+11 5432   # 0012: sipush #+5432
+12 08     # 0015: ldc <string>
+12 09     # 0017: ldc <integer>
+12 0a     # 0019: ldc <float>
+13 0008   # 001b: ldc_w <string>
+13 0009   # 001e: ldc_w <integer>
+13 000a   # 0021: ldc_w <float>
+14 000b   # 0024: ldc2_w <long>
+14 000d   # 0027: ldc2_w <double>
+15 01     # 002a: iload 01
+16 02     # 002c: lload 02
+17 03     # 002e: fload 03
+18 04     # 0030: dload 04
+19 05     # 0032: aload 05
+1a        # 0034: iload_0
+1b        # 0035: iload_1
+1c        # 0036: iload_2
+1d        # 0037: iload_3
+1e        # 0038: lload_0
+1f        # 0039: lload_1
+20        # 003a: lload_2
+21        # 003b: lload_3
+22        # 003c: fload_0
+23        # 003d: fload_1
+24        # 003e: fload_2
+25        # 003f: fload_3
+26        # 0040: dload_0
+27        # 0041: dload_1
+28        # 0042: dload_2
+29        # 0043: dload_3
+2a        # 0044: aload_0
+2b        # 0045: aload_1
+2c        # 0046: aload_2
+2d        # 0047: aload_3
+2e        # 0048: iaload
+2f        # 0049: laload
+30        # 004a: faload
+31        # 004b: daload
+32        # 004c: aaload
+33        # 004d: baload
+34        # 004e: caload
+35        # 004f: saload
+36 41     # 0050: istore 41
+37 42     # 0052: lstore 42
+38 43     # 0054: fstore 43
+39 44     # 0056: dstore 44
+3a 45     # 0058: astore 45
+3b        # 005a: istore_0
+3c        # 005b: istore_1
+3d        # 005c: istore_2
+3e        # 005d: istore_3
+3f        # 005e: lstore_0
+40        # 005f: lstore_1
+41        # 0060: lstore_2
+42        # 0061: lstore_3
+43        # 0062: fstore_0
+44        # 0063: fstore_1
+45        # 0064: fstore_2
+46        # 0065: fstore_3
+47        # 0066: dstore_0
+48        # 0067: dstore_1
+49        # 0068: dstore_2
+4a        # 0069: dstore_3
+4b        # 006a: astore_0
+4c        # 006b: astore_1
+4d        # 006c: astore_2
+4e        # 006d: astore_3
+4f        # 006e: iastore
+50        # 006f: lastore
+51        # 0070: fastore
+52        # 0071: dastore
+53        # 0072: aastore
+54        # 0073: bastore
+55        # 0074: castore
+56        # 0075: sastore
+57        # 0076: pop
+58        # 0077: pop2
+59        # 0078: dup
+5a        # 0079: dup_x1
+5b        # 007a: dup_x2
+5c        # 007b: dup2
+5d        # 007c: dup2_x1
+5e        # 007d: dup2_x2
+5f        # 007e: swap
+60        # 007f: iadd
+61        # 0080: ladd
+62        # 0081: fadd
+63        # 0082: dadd
+64        # 0083: isub
+65        # 0084: lsub
+66        # 0085: fsub
+67        # 0086: dsub
+68        # 0087: imul
+69        # 0088: lmul
+6a        # 0089: fmul
+6b        # 008a: dmul
+6c        # 008b: idiv
+6d        # 008c: ldiv
+6e        # 008d: fdiv
+6f        # 008e: ddiv
+70        # 008f: irem
+71        # 0090: lrem
+72        # 0091: frem
+73        # 0092: drem
+74        # 0093: ineg
+75        # 0094: lneg
+76        # 0095: fneg
+77        # 0096: dneg
+78        # 0097: ishl
+79        # 0098: lshl
+7a        # 0099: ishr
+7b        # 009a: lshr
+7c        # 009b: iushr
+7d        # 009c: lushr
+7e        # 009d: iand
+7f        # 009e: land
+80        # 009f: ior
+81        # 00a0: lor
+82        # 00a1: ixor
+83        # 00a2: lxor
+84 05 ff  # 00a3: iinc 05, #-1
+85        # 00a6: i2l
+86        # 00a7: i2f
+87        # 00a8: i2d
+88        # 00a9: l2i
+89        # 00aa: l2f
+8a        # 00ab: l2d
+8b        # 00ac: f2i
+8c        # 00ad: f2l
+8d        # 00ae: f2d
+8e        # 00af: d2i
+8f        # 00b0: d2l
+90        # 00b1: d2f
+91        # 00b2: i2b
+92        # 00b3: i2c
+93        # 00b4: i2s
+94        # 00b5: lcmp
+95        # 00b6: fcmpl
+96        # 00b7: fcmpg
+97        # 00b8: dcmpl
+98        # 00b9: dcmpg
+99 0000   # 00ba: ifeq 00ba
+9a fffd   # 00bd: ifne 00ba
+9b fffa   # 00c0: iflt 00ba
+9c fff7   # 00c3: ifge 00ba
+9d fff4   # 00c6: ifgt 00ba
+9e fff1   # 00c9: ifle 00ba
+9f 000f   # 00cc: if_icmpeq 00db
+a0 000c   # 00cf: if_icmpne 00db
+a1 0009   # 00d2: if_icmplt 00db
+a2 0006   # 00d5: if_icmpge 00db
+a3 0003   # 00d8: if_icmpgt 00db
+a4 0000   # 00db: if_icmple 00db
+a5 0000   # 00de: if_acmpeq 00de
+a6 0000   # 00e1: if_acmpne 00e1
+a7 ff1c   # 00e4: goto 0000
+a8 0000   # 00e7: jsr 00e7
+a9 2f     # 00ea: ret 2f
+aa 000000 # 00ec: tableswitch + padding
+ fffffffe #   default: 000000ea
+ 12340000 #   low: 12340000
+ 12340007 #   high: 12340007
+ ffffff14 #   [0]: 00000000
+ ffffff15 #   [1]: 00000001
+ ffffff16 #   [2]: 00000002
+ ffffff17 #   [3]: 00000003
+ ffffff18 #   [4]: 00000004
+ ffffff19 #   [5]: 00000005
+ ffffff1b #   [6]: 00000007
+ ffffff1d #   [7]: 00000009
+ab 000000 # 011c: lookupswitch + padding
+ ffffffd0 #   default: 000000ec
+ 00000004 #   npairs: 4
+ 89761234 #   match[0]: 89761234
+ 0000002c #   offset[0]: 0148
+ 00001000 #   match[1]: 00001000
+ 0000002d #   offset[1]: 0149
+ 03333333 #   match[2]: 03333333
+ 0000002d #   offset[2]: 0149
+ 79787776 #   match[3]: 79787776
+ 0000002f #   offset[3]: 014b
+ac        # 0148: ireturn
+ad        # 0149: lreturn
+ae        # 014a: freturn
+af        # 014b: dreturn
+b0        # 014c: areturn
+b1        # 014d: return
+b2 0014   # 014e: getstatic 0014
+b3 0014   # 0151: putstatic 0014
+b4 0014   # 0154: getfield 0014
+b5 0014   # 0157: putfield 0014
+b6 0015   # 015a: invokevirtual 0015
+b7 0015   # 015d: invokespecial 0015
+b8 0015   # 0160: invokestatic 0015
+b9 0016 01 00  # 0163: invokeinterface 0016
+ba        # 0168: <unused>
+bb 0001   # 0169: new 0001
+bc 04     # 016c: newarray boolean
+bc 05     # 016e: newarray char
+bc 06     # 0170: newarray float
+bc 07     # 0172: newarray double
+bc 08     # 0174: newarray byte
+bc 09     # 0176: newarray short
+bc 0a     # 0178: newarray int
+bc 0b     # 017a: newarray long
+bd 0001   # 017c: anewarray 0001
+be        # 017f: arraylength
+bf        # 0180: athrow
+c0 0002   # 0181: checkcast 0002
+c1 0002   # 0184: instanceof 0002
+c2        # 0187: monitorenter
+c3        # 0188: monitorexit
+c415 0123 # 0189: wide iload 0123
+c416 0124 # 018d: wide lload 0124
+c417 0125 # 0191: wide fload 0125
+c418 0126 # 0195: wide dload 0126
+c419 0127 # 0199: wide aload 0127
+c436 20f0 # 019d: wide istore 20f0
+c437 20f1 # 01a1: wide lstore 20f1
+c438 20f2 # 01a5: wide fstore 20f2
+c439 20f3 # 01a9: wide dstore 20f3
+c43a 20f4 # 01ad: wide astore 20f4
+c4a9 ffff # 01b1: wide ret ffff
+c484 0002 1000 # 01b5: wide iinc 0002, 1000
+c5 0002 04 # 01bb: multianewarray 0002, #04
+c6 fe41   # 01bf: ifnull 0000
+c7 0000   # 01c2: ifnonnull 01c2
+c8 70000000 # 01c5: goto_w 700001c5
+c9 fffffffb # 01ca: jsr_w 000001c5
+ca        # 01cf: <unused>
+
+0000      # exception_table_length
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/025-class-attrib-Signature/expected.txt b/dx/tests/025-class-attrib-Signature/expected.txt
new file mode 100644
index 0000000..5ff56ed
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Signature"}
+  0006: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Signature
+  length: 00000002
+  signature: utf8{"LYo;"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/025-class-attrib-Signature/info.txt b/dx/tests/025-class-attrib-Signature/info.txt
new file mode 100644
index 0000000..3d0ce51
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Signature attribute, which is syntactically valid.
diff --git a/dx/tests/025-class-attrib-Signature/run b/dx/tests/025-class-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/025-class-attrib-Signature/small-class.txt b/dx/tests/025-class-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..d332e3a
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Signature"        # 0005: utf8["Signature"]
+01 0004 "LYo;"             # 0006: utf8["Yo"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000002  # length
+0006      # signature
\ No newline at end of file
diff --git a/dx/tests/026-field-attrib-Signature/expected.txt b/dx/tests/026-field-attrib-Signature/expected.txt
new file mode 100644
index 0000000..c2e840e
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Signature"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+  0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Signature
+    length: 00000002
+    signature: utf8{"LYo;"}
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/026-field-attrib-Signature/info.txt b/dx/tests/026-field-attrib-Signature/info.txt
new file mode 100644
index 0000000..5b21ccb
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a syntactically valid Signature attribute.
diff --git a/dx/tests/026-field-attrib-Signature/run b/dx/tests/026-field-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/026-field-attrib-Signature/small-class.txt b/dx/tests/026-field-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..07d56a0
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/small-class.txt
@@ -0,0 +1,40 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Signature"        # 0005: utf8["Signature"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+01 0004 "LYo;"             # 0008: utf8["Yo"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0008      # signature
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/027-method-attrib-Signature/expected.txt b/dx/tests/027-method-attrib-Signature/expected.txt
new file mode 100644
index 0000000..abc97c0
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Signature"}
+  0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Signature
+    length: 00000002
+    signature: utf8{"LYo;"}
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/027-method-attrib-Signature/info.txt b/dx/tests/027-method-attrib-Signature/info.txt
new file mode 100644
index 0000000..ea18c03
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Signature attribute.
diff --git a/dx/tests/027-method-attrib-Signature/run b/dx/tests/027-method-attrib-Signature/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/027-method-attrib-Signature/small-class.txt b/dx/tests/027-method-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..c1cd6e3
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/small-class.txt
@@ -0,0 +1,39 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0009 "Signature"        # 0007: utf8["Signature"]
+01 0004 "LYo;"             # 0008: utf8["Yo"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000002  # length
+0008      # signature
+
+0000  # attributes_count
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/expected.txt b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
new file mode 100644
index 0000000..15e9524
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
@@ -0,0 +1,61 @@
+reading small-class-1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"EnclosingMethod"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: EnclosingMethod
+  length: 00000004
+  class: type{Small}
+  method: (none)
+end attributes[0]
+end classfile
+reading small-class-2.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"EnclosingMethod"}
+  0006: utf8{"zorp"}
+  0007: utf8{"()V"}
+  0008: nat{zorp:()V}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: EnclosingMethod
+  length: 00000004
+  class: type{Small}
+  method: nat{zorp:()V}
+end attributes[0]
+end classfile
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/info.txt b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
new file mode 100644
index 0000000..206a43e
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
@@ -0,0 +1,9 @@
+This is a dump of two simple classes which are valid in structure but
+are overall invalid. That being said, the system should still have no
+trouble parsing and dumping them.
+
+The salient bit of parsing tested here is that each class has a single
+class-level EnclosingMethod attribute, which is syntactically valid. There
+are two possible variants (method may be null), and this test contains one
+of each.
+
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/run b/dx/tests/028-class-attrib-EnclosingMethod/run
new file mode 100644
index 0000000..f33a338
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class-1.txt small-class-2.txt
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
new file mode 100644
index 0000000..aeaf2a3
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod"  # 0005: utf8["EnclosingMethod"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000004  # length
+0003      # class
+0000      # method
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
new file mode 100644
index 0000000..8e6148e
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
@@ -0,0 +1,35 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod"  # 0005: utf8["EnclosingMethod"]
+01 0004 "zorp"             # 0006: utf8["zorp"]
+01 0003 "()V"              # 0007: utf8["()V"]
+0c 0006 0007               # 0008: nat[zorp:()V]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000004  # length
+0003      # class
+0008      # method
diff --git a/dx/tests/029-unit-Bits/expected.txt b/dx/tests/029-unit-Bits/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/029-unit-Bits/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/029-unit-Bits/info.txt b/dx/tests/029-unit-Bits/info.txt
new file mode 100644
index 0000000..fa56715
--- /dev/null
+++ b/dx/tests/029-unit-Bits/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.Bits.
diff --git a/dx/tests/029-unit-Bits/run b/dx/tests/029-unit-Bits/run
new file mode 100644
index 0000000..9937ce5
--- /dev/null
+++ b/dx/tests/029-unit-Bits/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --junit com.android.dx.util._tests._Bits > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/030-minimal-jasmin/blort.j b/dx/tests/030-minimal-jasmin/blort.j
new file mode 100644
index 0000000..45722bc
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/blort.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    dup
+    dup
+    astore_1
+    pop2
+    return
+.end method
diff --git a/dx/tests/030-minimal-jasmin/expected.txt b/dx/tests/030-minimal-jasmin/expected.txt
new file mode 100644
index 0000000..de52b4d
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/expected.txt
@@ -0,0 +1,7 @@
+Generated: ./blort.class
+    0000: aload_0 // 00
+    0001: dup
+    0002: dup
+    0003: astore_1 // 01
+    0004: pop2
+    0005: return
diff --git a/dx/tests/030-minimal-jasmin/info.txt b/dx/tests/030-minimal-jasmin/info.txt
new file mode 100644
index 0000000..816c244
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/info.txt
@@ -0,0 +1,2 @@
+This test is just a minimal test involving assembling a jasmin source
+file and then dumping it. It doesn't test any features in particular.
diff --git a/dx/tests/030-minimal-jasmin/run b/dx/tests/030-minimal-jasmin/run
new file mode 100644
index 0000000..6a50596
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --width=200 blort.class | grep '    000.:'
diff --git a/dx/tests/031-bb-dead-code/blort.j b/dx/tests/031-bb-dead-code/blort.j
new file mode 100644
index 0000000..d3e20d0
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/blort.j
@@ -0,0 +1,183 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 1
+
+    aload_0
+    invokespecial java/lang/Object/<init>()V
+    return
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend1()V
+    return
+    aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend2()V
+    ireturn
+    aload_0
+    aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend3()V
+    aconst_null
+    athrow
+    sipush 0x1234
+.end method
+
+; make sure an exception handler for a dead range doesn't get enlivened
+.method public test_dead_exception_handler()V
+    return
+    nop
+blort:
+    nop
+    nop
+    return
+handler:
+    nop
+    return
+    .catch all from blort to handler using handler
+.end method
+
+; dead code after goto instruction
+.method public test_dead_goto()V
+    goto blort
+    nop
+blort:
+    return
+.end method
+
+; dead code after ret instruction
+.method public test_dead_ret()V
+    ifeq blort
+    ret 0
+    iconst_m1
+blort:
+    return
+.end method
+
+; dead code after tableswitch instruction
+.method public test_dead_tableswitch()V
+    tableswitch 0x10
+        blort
+        default: blort
+    nop
+    nop
+    nop
+    aload_0
+    aload_1
+    aload_2
+    aload_3
+blort:
+    return
+.end method
+
+; dead code after lookupswitch instruction
+.method public test_dead_lookupswitch()V
+    lookupswitch
+        0x10: blort
+        0x20: blort
+        default: blort
+    ldc "WHYA REYO UREA DING THIS ?"
+blort:
+    return
+.end method
+
+; dead code after ireturn instruction
+.method public test_dead_ireturn()V
+    ifeq blort
+    ireturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after lreturn instruction
+.method public test_dead_lreturn()V
+    ifeq blort
+    lreturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after freturn instruction
+.method public test_dead_freturn()V
+    ifeq blort
+    freturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after dreturn instruction
+.method public test_dead_dreturn()V
+    ifeq blort
+    dreturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after areturn instruction
+.method public test_dead_areturn()V
+    ifeq blort
+    areturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after return instruction
+.method public test_dead_return()V
+    ifeq blort
+    return
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after athrow instruction
+.method public test_dead_athrow()V
+    ifeq blort
+    athrow
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after wide ret instruction
+.method public test_dead_wideret()V
+    ifeq blort
+    ret 0x0100
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after goto_w instruction
+.method public test_dead_goto_w()V
+    goto_w blort
+    iconst_1
+blort:
+    return
+.end method
+
diff --git a/dx/tests/031-bb-dead-code/expected.txt b/dx/tests/031-bb-dead-code/expected.txt
new file mode 100644
index 0000000..1cd4959
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/expected.txt
@@ -0,0 +1,190 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+  0000: aload_0 // 00
+  0001: invokespecial method{java.lang.Object.<init>:()V}
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_deadend1 ()V
+block 0000: 0000..0001
+  0000: return
+  returns
+dead code 0001..0002
+
+method test_deadend2 ()V
+block 0000: 0000..0001
+  0000: ireturn
+  returns
+dead code 0001..0003
+
+method test_deadend3 ()V
+block 0000: 0000..0002
+  0000: aconst_null
+  0001: athrow
+  returns
+dead code 0002..0005
+
+method test_dead_exception_handler ()V
+block 0000: 0000..0001
+  0000: return
+  returns
+dead code 0001..0007
+
+method test_dead_goto ()V
+block 0000: 0000..0003
+  0000: goto 0004
+  next 0004
+dead code 0003..0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_dead_ret ()V
+block 0000: 0000..0003
+  0000: ifeq 0006
+  next 0003
+  next 0006
+block 0003: 0003..0005
+  0003: ret 00
+  returns
+dead code 0005..0006
+block 0006: 0006..0007
+  0006: return
+  returns
+
+method test_dead_tableswitch ()V
+block 0000: 0000..0014
+  0000: tableswitch
+    default: 001b
+  next 001b
+dead code 0014..001b
+block 001b: 001b..001c
+  001b: return
+  returns
+
+method test_dead_lookupswitch ()V
+block 0000: 0000..001c
+  0000: lookupswitch
+    default: 001e
+  next 001e
+dead code 001c..001e
+block 001e: 001e..001f
+  001e: return
+  returns
+
+method test_dead_ireturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: ireturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_lreturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: lreturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_freturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: freturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_dreturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: dreturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_areturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: areturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_return ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: return
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_athrow ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: athrow
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_wideret ()V
+block 0000: 0000..0003
+  0000: ifeq 0008
+  next 0003
+  next 0008
+block 0003: 0003..0007
+  0003: wide ret 0100
+  returns
+dead code 0007..0008
+block 0008: 0008..0009
+  0008: return
+  returns
+
+method test_dead_goto_w ()V
+block 0000: 0000..0005
+  0000: goto_w 00000006
+  next 0006
+dead code 0005..0006
+block 0006: 0006..0007
+  0006: return
+  returns
diff --git a/dx/tests/031-bb-dead-code/info.txt b/dx/tests/031-bb-dead-code/info.txt
new file mode 100644
index 0000000..d5aa398
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/info.txt
@@ -0,0 +1,3 @@
+This test checks to see that the basic block recognizer properly omits
+dead code. There is at least one example of dead code after each instruction 
+that *doesn't* flow to the next instruction.
diff --git a/dx/tests/031-bb-dead-code/run b/dx/tests/031-bb-dead-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/032-bb-live-code/blort.j b/dx/tests/032-bb-live-code/blort.j
new file mode 100644
index 0000000..a1ec1dc
--- /dev/null
+++ b/dx/tests/032-bb-live-code/blort.j
@@ -0,0 +1,343 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 1
+
+    aload_0
+    invokespecial java/lang/Object/<init>()V
+    return
+.end method
+
+; Test that an exception handler for a live range is enlivened.
+.method public test_live_exception([I)V
+    nop
+    nop
+start:
+    aload_0
+    arraylength
+end1:
+    nop
+end2:
+    return
+handler1:
+    return
+handler2:
+    return
+    .catch java/lang/RuntimeException from start to end2 using handler2
+    .catch all from start to end1 using handler1
+.end method
+
+; Test that an exception handler for a live range is dead as long as
+; the covered code can't possibly throw.
+.method public test_dead_exception()V
+    nop
+    nop
+start:
+    nop
+end1:
+    nop
+end2:
+    return
+handler1:
+    return
+handler2:
+    return
+    .catch java/lang/RuntimeException from start to end2 using handler2
+    .catch all from start to end1 using handler1
+.end method
+
+; Test all the if* variants.
+.method public test_ifs()V
+    ifeq x0
+    ifne x1
+    iflt x2
+    ifge x3
+    ifgt x4
+    ifle x5
+    if_icmpeq x6
+    if_icmpne x7
+    if_icmplt x8
+    if_icmpge x9
+    if_icmpgt x10
+    if_icmple x11
+    if_acmpeq x12
+    if_acmpne x13
+    ifnull x14
+    ifnonnull x15
+    return
+x0:
+    return
+x1:
+    return
+x2:
+    return
+x3:
+    return
+x4:
+    return
+x5:
+    return
+x6:
+    return
+x7:
+    return
+x8:
+    return
+x9:
+    return
+x10:
+    return
+x11:
+    return
+x12:
+    return
+x13:
+    return
+x14:
+    return
+x15:
+    return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()V
+    jsr j1
+    jsr_w j2
+    return
+j1:
+    astore_0
+    ret 0
+j2:
+    astore_0
+    ret_w 0
+.end method
+
+; Test tableswitch.
+.method public test_tableswitch()V
+    tableswitch 0x10
+        t1
+        t2
+        default: t3
+t1:
+    return
+t2:
+    return
+t3:
+    return
+.end method
+
+; Test lookupswitch.
+.method public test_lookupswitch()V
+    lookupswitch
+        0x05: s1
+        0x10: s2
+        default: s3
+s1:
+    return
+s2:
+    return
+s3:
+    return
+.end method
+
+; Test every non-branching op.
+.method public test_nonbranch()V
+    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 0x10
+    sipush 0x1000
+    ldc "x"
+    ldc_w "y"
+    ldc2_w 3.0
+    iload 5
+    lload 5
+    fload 5
+    dload 5
+    aload 5
+    iload_0
+    iload_1
+    iload_2
+    iload_3
+    lload_0
+    lload_1
+    lload_2
+    lload_3
+    fload_0
+    fload_1
+    fload_2
+    fload_3
+    dload_0
+    dload_1
+    dload_2
+    dload_3
+    aload_0
+    aload_1
+    aload_2
+    aload_3
+    iaload
+    laload
+    faload
+    daload
+    aaload
+    baload
+    caload
+    saload
+    istore 5
+    lstore 5
+    fstore 5
+    dstore 5
+    astore 5
+    istore_0
+    istore_1
+    istore_2
+    istore_3
+    lstore_0
+    lstore_1
+    lstore_2
+    lstore_3
+    fstore_0
+    fstore_1
+    fstore_2
+    fstore_3
+    dstore_0
+    dstore_1
+    dstore_2
+    dstore_3
+    astore_0
+    astore_1
+    astore_2
+    astore_3
+    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 5 0x10
+    i2l
+    i2f
+    i2d
+    l2i
+    l2f
+    l2d
+    f2i
+    f2l
+    f2d
+    d2i
+    d2l
+    d2f
+    i2b
+    i2c
+    i2s
+    lcmp
+    fcmpl
+    fcmpg
+    dcmpl
+    dcmpg
+    getstatic blort/x I
+    putstatic blort/x I
+    getfield blort/x I
+    putfield blort/x I
+    invokevirtual blort/x()V
+    invokespecial blort/x()V
+    invokestatic blort/x()V
+    invokeinterface blort/x()V 1
+    new blort
+    newarray int
+    anewarray blort
+    arraylength
+    checkcast blort
+    instanceof blort
+    monitorenter
+    monitorexit
+    iload 0x100
+    lload 0x100
+    fload 0x100
+    dload 0x100
+    aload 0x100
+    istore 0x100
+    lstore 0x100
+    fstore 0x100
+    dstore 0x100
+    astore 0x100
+    iinc 0x123 0x321
+    multianewarray [[[I 2
+    return
+.end method
+
diff --git a/dx/tests/032-bb-live-code/expected.txt b/dx/tests/032-bb-live-code/expected.txt
new file mode 100644
index 0000000..26444af
--- /dev/null
+++ b/dx/tests/032-bb-live-code/expected.txt
@@ -0,0 +1,497 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+  0000: aload_0 // 00
+  0001: invokespecial method{java.lang.Object.<init>:()V}
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_live_exception ([I)V
+block 0000: 0000..0002
+  0000: nop
+  0001: nop
+  next 0002
+block 0002: 0002..0004
+  0002: aload_0 // 00
+  0003: arraylength
+  next 0007
+  next 0006
+  next 0004
+  catch java.lang.RuntimeException -> 0007
+  catch <any> -> 0006
+block 0004: 0004..0005
+  0004: nop
+  next 0005
+block 0005: 0005..0006
+  0005: return
+  returns
+block 0006: 0006..0007
+  0006: return
+  returns
+block 0007: 0007..0008
+  0007: return
+  returns
+
+method test_dead_exception ()V
+block 0000: 0000..0002
+  0000: nop
+  0001: nop
+  next 0002
+block 0002: 0002..0003
+  0002: nop
+  next 0003
+block 0003: 0003..0004
+  0003: nop
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+block 0005: 0005..0006
+  0005: return
+  returns
+block 0006: 0006..0007
+  0006: return
+  returns
+
+method test_ifs ()V
+block 0000: 0000..0003
+  0000: ifeq 0031
+  next 0003
+  next 0031
+block 0003: 0003..0006
+  0003: ifne 0032
+  next 0006
+  next 0032
+block 0006: 0006..0009
+  0006: iflt 0033
+  next 0009
+  next 0033
+block 0009: 0009..000c
+  0009: ifge 0034
+  next 000c
+  next 0034
+block 000c: 000c..000f
+  000c: ifgt 0035
+  next 000f
+  next 0035
+block 000f: 000f..0012
+  000f: ifle 0036
+  next 0012
+  next 0036
+block 0012: 0012..0015
+  0012: if_icmpeq 0037
+  next 0015
+  next 0037
+block 0015: 0015..0018
+  0015: if_icmpne 0038
+  next 0018
+  next 0038
+block 0018: 0018..001b
+  0018: if_icmplt 0039
+  next 001b
+  next 0039
+block 001b: 001b..001e
+  001b: if_icmpge 003a
+  next 001e
+  next 003a
+block 001e: 001e..0021
+  001e: if_icmpgt 003b
+  next 0021
+  next 003b
+block 0021: 0021..0024
+  0021: if_icmple 003c
+  next 0024
+  next 003c
+block 0024: 0024..0027
+  0024: if_acmpeq 003d
+  next 0027
+  next 003d
+block 0027: 0027..002a
+  0027: if_acmpne 003e
+  next 002a
+  next 003e
+block 002a: 002a..002d
+  002a: ifnull 003f
+  next 002d
+  next 003f
+block 002d: 002d..0030
+  002d: ifnonnull 0040
+  next 0030
+  next 0040
+block 0030: 0030..0031
+  0030: return
+  returns
+block 0031: 0031..0032
+  0031: return
+  returns
+block 0032: 0032..0033
+  0032: return
+  returns
+block 0033: 0033..0034
+  0033: return
+  returns
+block 0034: 0034..0035
+  0034: return
+  returns
+block 0035: 0035..0036
+  0035: return
+  returns
+block 0036: 0036..0037
+  0036: return
+  returns
+block 0037: 0037..0038
+  0037: return
+  returns
+block 0038: 0038..0039
+  0038: return
+  returns
+block 0039: 0039..003a
+  0039: return
+  returns
+block 003a: 003a..003b
+  003a: return
+  returns
+block 003b: 003b..003c
+  003b: return
+  returns
+block 003c: 003c..003d
+  003c: return
+  returns
+block 003d: 003d..003e
+  003d: return
+  returns
+block 003e: 003e..003f
+  003e: return
+  returns
+block 003f: 003f..0040
+  003f: return
+  returns
+block 0040: 0040..0041
+  0040: return
+  returns
+
+method test_jsr ()V
+block 0000: 0000..0003
+  0000: jsr 0009
+  next 0003
+  next 0009
+block 0003: 0003..0008
+  0003: jsr_w 0000000c
+  next 0008
+  next 000c
+block 0008: 0008..0009
+  0008: return
+  returns
+block 0009: 0009..000c
+  0009: astore_0 // 00
+  000a: ret 00
+  returns
+block 000c: 000c..0011
+  000c: astore_0 // 00
+  000d: wide ret 0000
+  returns
+
+method test_tableswitch ()V
+block 0000: 0000..0018
+  0000: tableswitch
+    +00000010: 0018
+    +00000011: 0019
+    default: 001a
+  next 0018
+  next 0019
+  next 001a
+block 0018: 0018..0019
+  0018: return
+  returns
+block 0019: 0019..001a
+  0019: return
+  returns
+block 001a: 001a..001b
+  001a: return
+  returns
+
+method test_lookupswitch ()V
+block 0000: 0000..001c
+  0000: lookupswitch
+    +00000005: 001c
+    +00000010: 001d
+    default: 001e
+  next 001c
+  next 001d
+  next 001e
+block 001c: 001c..001d
+  001c: return
+  returns
+block 001d: 001d..001e
+  001d: return
+  returns
+block 001e: 001e..001f
+  001e: return
+  returns
+
+method test_nonbranch ()V
+block 0000: 0000..0017
+  0000: nop
+  0001: aconst_null
+  0002: iconst_m1 // #-01
+  0003: iconst_0 // #+00
+  0004: iconst_1 // #+01
+  0005: iconst_2 // #+02
+  0006: iconst_3 // #+03
+  0007: iconst_4 // #+04
+  0008: iconst_5 // #+05
+  0009: lconst_0 // +00
+  000a: lconst_1 // +01
+  000b: fconst_0 // 0.0
+  000c: fconst_1 // 1.0
+  000d: fconst_2 // 2.0
+  000e: dconst_0 // 0.0
+  000f: dconst_1 // 1.0
+  0010: bipush #+10
+  0012: sipush #+1000
+  0015: ldc string{"x"}
+  next 0017
+block 0017: 0017..001a
+  0017: ldc_w string{"y"}
+  next 001a
+block 001a: 001a..003c
+  001a: ldc2_w #4008000000000000 // 3.0
+  001d: iload 05
+  001f: lload 05 // category-2
+  0021: fload 05
+  0023: dload 05 // category-2
+  0025: aload 05
+  0027: iload_0 // 00
+  0028: iload_1 // 01
+  0029: iload_2 // 02
+  002a: iload_3 // 03
+  002b: lload_0 // 00, category-2
+  002c: lload_1 // 01, category-2
+  002d: lload_2 // 02, category-2
+  002e: lload_3 // 03, category-2
+  002f: fload_0 // 00
+  0030: fload_1 // 01
+  0031: fload_2 // 02
+  0032: fload_3 // 03
+  0033: dload_0 // 00, category-2
+  0034: dload_1 // 01, category-2
+  0035: dload_2 // 02, category-2
+  0036: dload_3 // 03, category-2
+  0037: aload_0 // 00
+  0038: aload_1 // 01
+  0039: aload_2 // 02
+  003a: aload_3 // 03
+  003b: iaload
+  next 003c
+block 003c: 003c..003d
+  003c: laload
+  next 003d
+block 003d: 003d..003e
+  003d: faload
+  next 003e
+block 003e: 003e..003f
+  003e: daload
+  next 003f
+block 003f: 003f..0040
+  003f: aaload
+  next 0040
+block 0040: 0040..0041
+  0040: baload
+  next 0041
+block 0041: 0041..0042
+  0041: caload
+  next 0042
+block 0042: 0042..0043
+  0042: saload
+  next 0043
+block 0043: 0043..0062
+  0043: istore 05
+  0045: lstore 05 // category-2
+  0047: fstore 05
+  0049: dstore 05 // category-2
+  004b: astore 05
+  004d: istore_0 // 00
+  004e: istore_1 // 01
+  004f: istore_2 // 02
+  0050: istore_3 // 03
+  0051: lstore_0 // 00, category-2
+  0052: lstore_1 // 01, category-2
+  0053: lstore_2 // 02, category-2
+  0054: lstore_3 // 03, category-2
+  0055: fstore_0 // 00
+  0056: fstore_1 // 01
+  0057: fstore_2 // 02
+  0058: fstore_3 // 03
+  0059: dstore_0 // 00, category-2
+  005a: dstore_1 // 01, category-2
+  005b: dstore_2 // 02, category-2
+  005c: dstore_3 // 03, category-2
+  005d: astore_0 // 00
+  005e: astore_1 // 01
+  005f: astore_2 // 02
+  0060: astore_3 // 03
+  0061: iastore
+  next 0062
+block 0062: 0062..0063
+  0062: lastore
+  next 0063
+block 0063: 0063..0064
+  0063: fastore
+  next 0064
+block 0064: 0064..0065
+  0064: dastore
+  next 0065
+block 0065: 0065..0066
+  0065: aastore
+  next 0066
+block 0066: 0066..0067
+  0066: bastore
+  next 0067
+block 0067: 0067..0068
+  0067: castore
+  next 0068
+block 0068: 0068..0069
+  0068: sastore
+  next 0069
+block 0069: 0069..007f
+  0069: pop
+  006a: pop2
+  006b: dup
+  006c: dup_x1
+  006d: dup_x2
+  006e: dup2
+  006f: dup2_x1
+  0070: dup2_x2
+  0071: swap
+  0072: iadd
+  0073: ladd
+  0074: fadd
+  0075: dadd
+  0076: isub
+  0077: lsub
+  0078: fsub
+  0079: dsub
+  007a: imul
+  007b: lmul
+  007c: fmul
+  007d: dmul
+  007e: idiv
+  next 007f
+block 007f: 007f..0080
+  007f: ldiv
+  next 0080
+block 0080: 0080..0083
+  0080: fdiv
+  0081: ddiv
+  0082: irem
+  next 0083
+block 0083: 0083..0084
+  0083: lrem
+  next 0084
+block 0084: 0084..00b0
+  0084: frem
+  0085: drem
+  0086: ineg
+  0087: lneg
+  0088: fneg
+  0089: dneg
+  008a: ishl
+  008b: lshl
+  008c: ishr
+  008d: lshr
+  008e: iushr
+  008f: lushr
+  0090: iand
+  0091: land
+  0092: ior
+  0093: lor
+  0094: ixor
+  0095: lxor
+  0096: iinc 05, #+10
+  0099: i2l
+  009a: i2f
+  009b: i2d
+  009c: l2i
+  009d: l2f
+  009e: l2d
+  009f: f2i
+  00a0: f2l
+  00a1: f2d
+  00a2: d2i
+  00a3: d2l
+  00a4: d2f
+  00a5: i2b
+  00a6: i2c
+  00a7: i2s
+  00a8: lcmp
+  00a9: fcmpl
+  00aa: fcmpg
+  00ab: dcmpl
+  00ac: dcmpg
+  00ad: getstatic field{blort.x:I}
+  next 00b0
+block 00b0: 00b0..00b3
+  00b0: putstatic field{blort.x:I}
+  next 00b3
+block 00b3: 00b3..00b6
+  00b3: getfield field{blort.x:I}
+  next 00b6
+block 00b6: 00b6..00b9
+  00b6: putfield field{blort.x:I}
+  next 00b9
+block 00b9: 00b9..00bc
+  00b9: invokevirtual method{blort.x:()V}
+  next 00bc
+block 00bc: 00bc..00bf
+  00bc: invokespecial method{blort.x:()V}
+  next 00bf
+block 00bf: 00bf..00c2
+  00bf: invokestatic method{blort.x:()V}
+  next 00c2
+block 00c2: 00c2..00c7
+  00c2: invokeinterface ifaceMethod{blort.x:()V}, 0001
+  next 00c7
+block 00c7: 00c7..00ca
+  00c7: new type{blort}
+  next 00ca
+block 00ca: 00ca..00cc
+  00ca: newarray int
+  next 00cc
+block 00cc: 00cc..00cf
+  00cc: anewarray type{blort}
+  next 00cf
+block 00cf: 00cf..00d0
+  00cf: arraylength
+  next 00d0
+block 00d0: 00d0..00d3
+  00d0: checkcast type{blort}
+  next 00d3
+block 00d3: 00d3..00d6
+  00d3: instanceof type{blort}
+  next 00d6
+block 00d6: 00d6..00d7
+  00d6: monitorenter
+  next 00d7
+block 00d7: 00d7..00d8
+  00d7: monitorexit
+  next 00d8
+block 00d8: 00d8..010a
+  00d8: wide iload 0100
+  00dc: wide lload 0100 // category-2
+  00e0: wide fload 0100
+  00e4: wide dload 0100 // category-2
+  00e8: wide aload 0100
+  00ec: wide istore 0100
+  00f0: wide lstore 0100 // category-2
+  00f4: wide fstore 0100
+  00f8: wide dstore 0100 // category-2
+  00fc: wide astore 0100
+  0100: wide iinc 0123, #+0321
+  0106: multianewarray type{int[][][]}, 02
+  next 010a
+block 010a: 010a..010b
+  010a: return
+  returns
diff --git a/dx/tests/032-bb-live-code/info.txt b/dx/tests/032-bb-live-code/info.txt
new file mode 100644
index 0000000..f94b435
--- /dev/null
+++ b/dx/tests/032-bb-live-code/info.txt
@@ -0,0 +1,5 @@
+This test checks to see that the basic block recognizer properly
+includes as live code all code which could possibly be flowed
+to. There is at least one example of each instruction which allows
+flow to the subsequent instruction, and all forks of each conditional
+branch are checked for liveness as well.
diff --git a/dx/tests/032-bb-live-code/run b/dx/tests/032-bb-live-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/032-bb-live-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/033-unit-IntList/expected.txt b/dx/tests/033-unit-IntList/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/033-unit-IntList/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/033-unit-IntList/info.txt b/dx/tests/033-unit-IntList/info.txt
new file mode 100644
index 0000000..7d7e8aa
--- /dev/null
+++ b/dx/tests/033-unit-IntList/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.IntList.
diff --git a/dx/tests/033-unit-IntList/run b/dx/tests/033-unit-IntList/run
new file mode 100644
index 0000000..16ca6fb
--- /dev/null
+++ b/dx/tests/033-unit-IntList/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --junit com.android.dx.util._tests._IntList > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/034-dex-minimal/blort.j b/dx/tests/034-dex-minimal/blort.j
new file mode 100644
index 0000000..b4715b2
--- /dev/null
+++ b/dx/tests/034-dex-minimal/blort.j
@@ -0,0 +1,16 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
diff --git a/dx/tests/034-dex-minimal/expected.txt b/dx/tests/034-dex-minimal/expected.txt
new file mode 100644
index 0000000..a8f81c8
--- /dev/null
+++ b/dx/tests/034-dex-minimal/expected.txt
@@ -0,0 +1,70 @@
+000000: 6465 780a 3033|magic: "dex\n035\0"
+000006: 3500          |
+000008: be0b 70d9     |checksum
+00000c: 1d9c 3f88 730d|signature
+000012: 0ed6 caa3 77d4|
+000018: 5204 65e7 322d|
+00001e: 365a          |
+000020: 8c00 0000     |file_size:       0000008c
+000024: 7000 0000     |header_size:     00000070
+000028: 7856 3412     |endian_tag:      12345678
+00002c: 0000 0000     |link_size:       0
+000030: 0000 0000     |link_off:        0
+000034: 7000 0000     |map_off:         00000070
+000038: 0000 0000     |string_ids_size: 00000000
+00003c: 0000 0000     |string_ids_off:  00000000
+000040: 0000 0000     |type_ids_size:   00000000
+000044: 0000 0000     |type_ids_off:    00000000
+000048: 0000 0000     |proto_ids_size:  00000000
+00004c: 0000 0000     |proto_ids_off:   00000000
+000050: 0000 0000     |field_ids_size:  00000000
+000054: 0000 0000     |field_ids_off:   00000000
+000058: 0000 0000     |method_ids_size: 00000000
+00005c: 0000 0000     |method_ids_off:  00000000
+000060: 0000 0000     |class_defs_size: 00000000
+000064: 0000 0000     |class_defs_off:  00000000
+000068: 1c00 0000     |data_size:       0000001c
+00006c: 7000 0000     |data_off:        00000070
+                      |
+                      |string_ids:
+                      |
+                      |type_ids:
+                      |
+                      |proto_ids:
+                      |
+                      |field_ids:
+                      |
+                      |method_ids:
+                      |
+                      |class_defs:
+                      |
+                      |word_data:
+                      |
+                      |
+                      |string_data:
+                      |
+                      |byte_data:
+                      |
+                      |
+                      |map:
+                      |[70] map list
+000070: 0200 0000     |  size: 00000002
+                      |[74] header_item map
+000074: 0000          |  type:   0000 // TYPE_HEADER_ITEM
+000076: 0000          |  unused: 0
+000078: 0100 0000     |  size:   00000001
+00007c: 0000 0000     |  offset: 00000000
+                      |[80] map_list map
+000080: 0010          |  type:   1000 // TYPE_MAP_LIST
+000082: 0000          |  unused: 0
+000084: 0100 0000     |  size:   00000001
+000088: 7000 0000     |  offset: 00000070
+                      |
+                      |statistics:
+                      |  header: 1 item; 112 bytes total
+                      |    112 bytes/item
+                      |  map list: 1 item; 28 bytes total
+                      |    28 bytes/item
+
+processing blort.class...
+Good!
diff --git a/dx/tests/034-dex-minimal/info.txt b/dx/tests/034-dex-minimal/info.txt
new file mode 100644
index 0000000..13bb9ab
--- /dev/null
+++ b/dx/tests/034-dex-minimal/info.txt
@@ -0,0 +1,9 @@
+This is a smoke test of a couple cases of minimal dex creation:
+
+The first test tries to create the truly minimal dex file by using the
+--no-files option. The output dump of this is checked to make sure it
+is exactly correct. (There is arguably only one "right" answer.)
+
+The second test tries to convert a minimal classfile and ensures that
+the conversion runs without failure, though the contents of the
+converted file are not checked for correctness.
diff --git a/dx/tests/034-dex-minimal/run b/dx/tests/034-dex-minimal/run
new file mode 100644
index 0000000..bf90624
--- /dev/null
+++ b/dx/tests/034-dex-minimal/run
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dex --dump-to=- --no-files
+echo ""
+
+jasmin -d . blort.j >/dev/null
+dx --verbose --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/035-dex-instance-var/blort.j b/dx/tests/035-dex-instance-var/blort.j
new file mode 100644
index 0000000..1bcdf8e
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public x I
diff --git a/dx/tests/035-dex-instance-var/expected.txt b/dx/tests/035-dex-instance-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/035-dex-instance-var/info.txt b/dx/tests/035-dex-instance-var/info.txt
new file mode 100644
index 0000000..294c9eb
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance variable.
diff --git a/dx/tests/035-dex-instance-var/run b/dx/tests/035-dex-instance-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/036-dex-static-var/blort.j b/dx/tests/036-dex-static-var/blort.j
new file mode 100644
index 0000000..260f446
--- /dev/null
+++ b/dx/tests/036-dex-static-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public static x I
diff --git a/dx/tests/036-dex-static-var/expected.txt b/dx/tests/036-dex-static-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/036-dex-static-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/036-dex-static-var/info.txt b/dx/tests/036-dex-static-var/info.txt
new file mode 100644
index 0000000..5c562eb
--- /dev/null
+++ b/dx/tests/036-dex-static-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static variable.
diff --git a/dx/tests/036-dex-static-var/run b/dx/tests/036-dex-static-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/036-dex-static-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/037-dex-static-final-var/blort.j b/dx/tests/037-dex-static-final-var/blort.j
new file mode 100644
index 0000000..ac0332e
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public static final x I = 10
diff --git a/dx/tests/037-dex-static-final-var/expected.txt b/dx/tests/037-dex-static-final-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/037-dex-static-final-var/info.txt b/dx/tests/037-dex-static-final-var/info.txt
new file mode 100644
index 0000000..03d798f
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a final static variable.
diff --git a/dx/tests/037-dex-static-final-var/run b/dx/tests/037-dex-static-final-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/038-dex-instance-method/blort.j b/dx/tests/038-dex-instance-method/blort.j
new file mode 100644
index 0000000..7ddd7b5
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public blort()V
+    nop
+    return
+.end method
diff --git a/dx/tests/038-dex-instance-method/expected.txt b/dx/tests/038-dex-instance-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/038-dex-instance-method/info.txt b/dx/tests/038-dex-instance-method/info.txt
new file mode 100644
index 0000000..c9ce0b1
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/info.txt
@@ -0,0 +1,5 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance method.
+
diff --git a/dx/tests/038-dex-instance-method/run b/dx/tests/038-dex-instance-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/039-dex-static-method/blort.j b/dx/tests/039-dex-static-method/blort.j
new file mode 100644
index 0000000..5b74c57
--- /dev/null
+++ b/dx/tests/039-dex-static-method/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public static blort()V
+    nop
+    return
+.end method
diff --git a/dx/tests/039-dex-static-method/expected.txt b/dx/tests/039-dex-static-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/039-dex-static-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/039-dex-static-method/info.txt b/dx/tests/039-dex-static-method/info.txt
new file mode 100644
index 0000000..59c8d6e
--- /dev/null
+++ b/dx/tests/039-dex-static-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static method.
diff --git a/dx/tests/039-dex-static-method/run b/dx/tests/039-dex-static-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/039-dex-static-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/040-dex-constructor/blort.j b/dx/tests/040-dex-constructor/blort.j
new file mode 100644
index 0000000..e0ae441
--- /dev/null
+++ b/dx/tests/040-dex-constructor/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    nop
+    return
+.end method
diff --git a/dx/tests/040-dex-constructor/expected.txt b/dx/tests/040-dex-constructor/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/040-dex-constructor/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/040-dex-constructor/info.txt b/dx/tests/040-dex-constructor/info.txt
new file mode 100644
index 0000000..65eef93
--- /dev/null
+++ b/dx/tests/040-dex-constructor/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a constructor.
diff --git a/dx/tests/040-dex-constructor/run b/dx/tests/040-dex-constructor/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/040-dex-constructor/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/041-dex-abstract-method/blort.j b/dx/tests/041-dex-abstract-method/blort.j
new file mode 100644
index 0000000..8074fae
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/blort.j
@@ -0,0 +1,19 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public abstract blort()V
+.end method
diff --git a/dx/tests/041-dex-abstract-method/expected.txt b/dx/tests/041-dex-abstract-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/041-dex-abstract-method/info.txt b/dx/tests/041-dex-abstract-method/info.txt
new file mode 100644
index 0000000..e004388
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an abstract instance method.
diff --git a/dx/tests/041-dex-abstract-method/run b/dx/tests/041-dex-abstract-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/042-dex-ignore-result/Blort.java b/dx/tests/042-dex-ignore-result/Blort.java
new file mode 100644
index 0000000..21370ed
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    static public int hello() {
+        return 10;
+    }
+
+    static public void ouch() {
+        hello();
+        hello();
+    }
+}
diff --git a/dx/tests/042-dex-ignore-result/expected.txt b/dx/tests/042-dex-ignore-result/expected.txt
new file mode 100644
index 0000000..8ba83ea
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/expected.txt
@@ -0,0 +1,2 @@
+javac 1.7.0-internal_bootstrap
+Good!
diff --git a/dx/tests/042-dex-ignore-result/info.txt b/dx/tests/042-dex-ignore-result/info.txt
new file mode 100644
index 0000000..6289664
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/info.txt
@@ -0,0 +1,5 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of the case of a
+method call to a method which returns a value, where that value is
+ignored.
diff --git a/dx/tests/042-dex-ignore-result/run b/dx/tests/042-dex-ignore-result/run
new file mode 100644
index 0000000..d035d09
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --output=blort.dex Blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/043-dex-two-classes/Blort.java b/dx/tests/043-dex-two-classes/Blort.java
new file mode 100644
index 0000000..1d9de84
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Blort.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/Zorch.java b/dx/tests/043-dex-two-classes/Zorch.java
new file mode 100644
index 0000000..c9dc4eb
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Zorch.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/expected.txt b/dx/tests/043-dex-two-classes/expected.txt
new file mode 100644
index 0000000..82ec25d
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/expected.txt
@@ -0,0 +1,4 @@
+javac 1.7.0-internal_bootstrap
+processing Blort.class...
+processing Zorch.class...
+Good!
diff --git a/dx/tests/043-dex-two-classes/info.txt b/dx/tests/043-dex-two-classes/info.txt
new file mode 100644
index 0000000..785fb9b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test just makes sure that
+an attempt to combine two classes into a .dex file succeeds.
diff --git a/dx/tests/043-dex-two-classes/run b/dx/tests/043-dex-two-classes/run
new file mode 100644
index 0000000..41d3a0b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java Zorch.java
+dx --debug --verbose --dex --output=blort.dex *.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/044-dex-math-ops/Blort.java b/dx/tests/044-dex-math-ops/Blort.java
new file mode 100644
index 0000000..bf73c33
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    private volatile int i;
+    private volatile long l;
+    private volatile float f;
+    private volatile double d;
+    
+    public void blort(int i1, int i2) {
+        i = -i1;
+        i = ~i1;
+        i = i1 + i2;
+        i = i1 - i2;
+        i = i1 * i2;
+        i = i1 / i2;
+        i = i1 % i2;
+        i = i1 & i2;
+        i = i1 | i2;
+        i = i1 ^ i2;
+        i = i1 << i2;
+        i = i1 >> i2;
+        i = i1 >>> i2;
+    }
+
+    public void blort(long l1, long l2) {
+        l = -l1;
+        l = ~l1;
+        l = l1 + l2;
+        l = l1 - l2;
+        l = l1 * l2;
+        l = l1 / l2;
+        l = l1 % l2;
+        l = l1 & l2;
+        l = l1 | l2;
+        l = l1 ^ l2;
+        l = l1 << l2;
+        l = l1 >> l2;
+        l = l1 >>> l2;
+    }
+
+    public void blort(float f1, float f2) {
+        f = -f1;
+        f = f1 + f2;
+        f = f1 - f2;
+        f = f1 * f2;
+        f = f1 / f2;
+        f = f1 % f2;
+    }
+
+    public void blort(double d1, double d2) {
+        d = -d1;
+        d = d1 + d2;
+        d = d1 - d2;
+        d = d1 * d2;
+        d = d1 / d2;
+        d = d1 % d2;
+    }    
+}
diff --git a/dx/tests/044-dex-math-ops/expected.txt b/dx/tests/044-dex-math-ops/expected.txt
new file mode 100644
index 0000000..7fe1805
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/expected.txt
@@ -0,0 +1,214 @@
+javac 1.7.0-internal_bootstrap
+Blort.blort:(DD)V:
+regs: 000f; ins: 0005; outs: 0000
+  0000: move-object v0, v10
+  0001: move-wide v1, v11
+  0002: move-wide v3, v13
+  0003: move-object v5, v0
+  0004: move-wide v6, v1
+  0005: neg-double v6, v6
+  0006: iput-wide v6, v5, Blort.d:D
+  0008: move-object v5, v0
+  0009: move-wide v6, v1
+  000a: move-wide v8, v3
+  000b: add-double/2addr v6, v8
+  000c: iput-wide v6, v5, Blort.d:D
+  000e: move-object v5, v0
+  000f: move-wide v6, v1
+  0010: move-wide v8, v3
+  0011: sub-double/2addr v6, v8
+  0012: iput-wide v6, v5, Blort.d:D
+  0014: move-object v5, v0
+  0015: move-wide v6, v1
+  0016: move-wide v8, v3
+  0017: mul-double/2addr v6, v8
+  0018: iput-wide v6, v5, Blort.d:D
+  001a: move-object v5, v0
+  001b: move-wide v6, v1
+  001c: move-wide v8, v3
+  001d: div-double/2addr v6, v8
+  001e: iput-wide v6, v5, Blort.d:D
+  0020: move-object v5, v0
+  0021: move-wide v6, v1
+  0022: move-wide v8, v3
+  0023: rem-double/2addr v6, v8
+  0024: iput-wide v6, v5, Blort.d:D
+  0026: return-void
+Blort.blort:(FF)V:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move-object v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: move-object v3, v0
+  0004: move v4, v1
+  0005: neg-float v4, v4
+  0006: iput v4, v3, Blort.f:F
+  0008: move-object v3, v0
+  0009: move v4, v1
+  000a: move v5, v2
+  000b: add-float/2addr v4, v5
+  000c: iput v4, v3, Blort.f:F
+  000e: move-object v3, v0
+  000f: move v4, v1
+  0010: move v5, v2
+  0011: sub-float/2addr v4, v5
+  0012: iput v4, v3, Blort.f:F
+  0014: move-object v3, v0
+  0015: move v4, v1
+  0016: move v5, v2
+  0017: mul-float/2addr v4, v5
+  0018: iput v4, v3, Blort.f:F
+  001a: move-object v3, v0
+  001b: move v4, v1
+  001c: move v5, v2
+  001d: div-float/2addr v4, v5
+  001e: iput v4, v3, Blort.f:F
+  0020: move-object v3, v0
+  0021: move v4, v1
+  0022: move v5, v2
+  0023: rem-float/2addr v4, v5
+  0024: iput v4, v3, Blort.f:F
+  0026: return-void
+Blort.blort:(II)V:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move-object v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: move-object v3, v0
+  0004: move v4, v1
+  0005: neg-int v4, v4
+  0006: iput v4, v3, Blort.i:I
+  0008: move-object v3, v0
+  0009: move v4, v1
+  000a: const/4 v5, #int -1 // #f
+  000b: xor-int/lit8 v4, v4, #int -1 // #ff
+  000d: iput v4, v3, Blort.i:I
+  000f: move-object v3, v0
+  0010: move v4, v1
+  0011: move v5, v2
+  0012: add-int/2addr v4, v5
+  0013: iput v4, v3, Blort.i:I
+  0015: move-object v3, v0
+  0016: move v4, v1
+  0017: move v5, v2
+  0018: sub-int/2addr v4, v5
+  0019: iput v4, v3, Blort.i:I
+  001b: move-object v3, v0
+  001c: move v4, v1
+  001d: move v5, v2
+  001e: mul-int/2addr v4, v5
+  001f: iput v4, v3, Blort.i:I
+  0021: move-object v3, v0
+  0022: move v4, v1
+  0023: move v5, v2
+  0024: div-int/2addr v4, v5
+  0025: iput v4, v3, Blort.i:I
+  0027: move-object v3, v0
+  0028: move v4, v1
+  0029: move v5, v2
+  002a: rem-int/2addr v4, v5
+  002b: iput v4, v3, Blort.i:I
+  002d: move-object v3, v0
+  002e: move v4, v1
+  002f: move v5, v2
+  0030: and-int/2addr v4, v5
+  0031: iput v4, v3, Blort.i:I
+  0033: move-object v3, v0
+  0034: move v4, v1
+  0035: move v5, v2
+  0036: or-int/2addr v4, v5
+  0037: iput v4, v3, Blort.i:I
+  0039: move-object v3, v0
+  003a: move v4, v1
+  003b: move v5, v2
+  003c: xor-int/2addr v4, v5
+  003d: iput v4, v3, Blort.i:I
+  003f: move-object v3, v0
+  0040: move v4, v1
+  0041: move v5, v2
+  0042: shl-int/2addr v4, v5
+  0043: iput v4, v3, Blort.i:I
+  0045: move-object v3, v0
+  0046: move v4, v1
+  0047: move v5, v2
+  0048: shr-int/2addr v4, v5
+  0049: iput v4, v3, Blort.i:I
+  004b: move-object v3, v0
+  004c: move v4, v1
+  004d: move v5, v2
+  004e: ushr-int/2addr v4, v5
+  004f: iput v4, v3, Blort.i:I
+  0051: return-void
+Blort.blort:(JJ)V:
+regs: 000f; ins: 0005; outs: 0000
+  0000: move-object v0, v10
+  0001: move-wide v1, v11
+  0002: move-wide v3, v13
+  0003: move-object v5, v0
+  0004: move-wide v6, v1
+  0005: neg-long v6, v6
+  0006: iput-wide v6, v5, Blort.l:J
+  0008: move-object v5, v0
+  0009: move-wide v6, v1
+  000a: const-wide/16 v8, #long -1 // #ffff
+  000c: xor-long/2addr v6, v8
+  000d: iput-wide v6, v5, Blort.l:J
+  000f: move-object v5, v0
+  0010: move-wide v6, v1
+  0011: move-wide v8, v3
+  0012: add-long/2addr v6, v8
+  0013: iput-wide v6, v5, Blort.l:J
+  0015: move-object v5, v0
+  0016: move-wide v6, v1
+  0017: move-wide v8, v3
+  0018: sub-long/2addr v6, v8
+  0019: iput-wide v6, v5, Blort.l:J
+  001b: move-object v5, v0
+  001c: move-wide v6, v1
+  001d: move-wide v8, v3
+  001e: mul-long/2addr v6, v8
+  001f: iput-wide v6, v5, Blort.l:J
+  0021: move-object v5, v0
+  0022: move-wide v6, v1
+  0023: move-wide v8, v3
+  0024: div-long/2addr v6, v8
+  0025: iput-wide v6, v5, Blort.l:J
+  0027: move-object v5, v0
+  0028: move-wide v6, v1
+  0029: move-wide v8, v3
+  002a: rem-long/2addr v6, v8
+  002b: iput-wide v6, v5, Blort.l:J
+  002d: move-object v5, v0
+  002e: move-wide v6, v1
+  002f: move-wide v8, v3
+  0030: and-long/2addr v6, v8
+  0031: iput-wide v6, v5, Blort.l:J
+  0033: move-object v5, v0
+  0034: move-wide v6, v1
+  0035: move-wide v8, v3
+  0036: or-long/2addr v6, v8
+  0037: iput-wide v6, v5, Blort.l:J
+  0039: move-object v5, v0
+  003a: move-wide v6, v1
+  003b: move-wide v8, v3
+  003c: xor-long/2addr v6, v8
+  003d: iput-wide v6, v5, Blort.l:J
+  003f: move-object v5, v0
+  0040: move-wide v6, v1
+  0041: move-wide v8, v3
+  0042: long-to-int v8, v8
+  0043: shl-long/2addr v6, v8
+  0044: iput-wide v6, v5, Blort.l:J
+  0046: move-object v5, v0
+  0047: move-wide v6, v1
+  0048: move-wide v8, v3
+  0049: long-to-int v8, v8
+  004a: shr-long/2addr v6, v8
+  004b: iput-wide v6, v5, Blort.l:J
+  004d: move-object v5, v0
+  004e: move-wide v6, v1
+  004f: move-wide v8, v3
+  0050: long-to-int v8, v8
+  0051: ushr-long/2addr v6, v8
+  0052: iput-wide v6, v5, Blort.l:J
+  0054: return-void
diff --git a/dx/tests/044-dex-math-ops/info.txt b/dx/tests/044-dex-math-ops/info.txt
new file mode 100644
index 0000000..733dcb6
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple uses of all the math ops end up getting converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/044-dex-math-ops/run b/dx/tests/044-dex-math-ops/run
new file mode 100644
index 0000000..d8d4273
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.blort *.class
diff --git a/dx/tests/045-dex-switch-ops/Blort.java b/dx/tests/045-dex-switch-ops/Blort.java
new file mode 100644
index 0000000..fed741f
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/Blort.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public int switchTest1(int x) {
+        switch (x) {
+            case 1: {
+                return 2;
+            }
+            case 2: {
+                return 3;
+            }
+            case 3: {
+                return 4;
+            }
+            case 4: {
+                return 5;
+            }
+        }
+
+        return 6;
+    }
+
+    public int switchTest2(int x) {
+        switch (x) {
+            case 1: {
+                return 2;
+            }
+            case 10: {
+                return 3;
+            }
+            case 100: {
+                return 4;
+            }
+            case 1000: {
+                return 50;
+            }
+        }
+
+        return 6;
+    }
+}
diff --git a/dx/tests/045-dex-switch-ops/expected.txt b/dx/tests/045-dex-switch-ops/expected.txt
new file mode 100644
index 0000000..72c571b
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/expected.txt
@@ -0,0 +1,54 @@
+javac 1.7.0-internal_bootstrap
+Blort.switchTest1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 0016 // +0013
+  0006: const/4 v2, #int 6 // #6
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 2 // #2
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 3 // #3
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: const/4 v2, #int 4 // #4
+  0010: move v0, v2
+  0011: goto 0008 // -0009
+  0012: const/4 v2, #int 5 // #5
+  0013: move v0, v2
+  0014: goto 0008 // -000c
+  0015: nop // spacer
+  0016: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 0000000c // +00000009
+          3: 0000000f // +0000000c
+          4: 00000012 // +0000000f
+Blort.switchTest2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0016 // +0013
+  0006: const/4 v2, #int 6 // #6
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 2 // #2
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 3 // #3
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: const/4 v2, #int 4 // #4
+  0010: move v0, v2
+  0011: goto 0008 // -0009
+  0012: const/16 v2, #int 50 // #0032
+  0014: move v0, v2
+  0015: goto 0008 // -000d
+  0016: sparse-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          10: 0000000c // +00000009
+          100: 0000000f // +0000000c
+          1000: 00000012 // +0000000f
diff --git a/dx/tests/045-dex-switch-ops/info.txt b/dx/tests/045-dex-switch-ops/info.txt
new file mode 100644
index 0000000..492dc0b
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+both kinds of switch op get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/045-dex-switch-ops/run b/dx/tests/045-dex-switch-ops/run
new file mode 100644
index 0000000..8d1b65e
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.switchTest'*' *.class
diff --git a/dx/tests/046-dex-exceptions/Blort.java b/dx/tests/046-dex-exceptions/Blort.java
new file mode 100644
index 0000000..8d040c4
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/Blort.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int maybeThrow(int x) {
+        if (x < 10) {
+            throw new RuntimeException();
+        }
+
+        return x;
+    }
+    
+    public static int exTest1(int x) {
+        try {
+            maybeThrow(x);
+            return 1;
+        } catch (RuntimeException ex) {
+            return 2;
+        }
+    }
+
+    public static int exTest2(int x) {
+        try {
+            x++;
+            x = maybeThrow(x);
+        } catch (RuntimeException ex) {
+            return 1;
+        }
+
+        // Since the code in the try block can't possibly throw, there
+        // should not be a catch in the final result.
+        try {
+            x++;
+        } catch (RuntimeException ex) {
+            return 2;
+        }
+
+        try {
+            return maybeThrow(x);
+        } catch (RuntimeException ex) {
+            return 3;
+        }
+    }
+}
diff --git a/dx/tests/046-dex-exceptions/expected.txt b/dx/tests/046-dex-exceptions/expected.txt
new file mode 100644
index 0000000..5eddfcb
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/expected.txt
@@ -0,0 +1,49 @@
+javac 1.7.0-internal_bootstrap
+Blort.exTest1:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: invoke-static {v2}, Blort.maybeThrow:(I)I
+  0005: move-result v2
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: move-exception v2
+  000a: move-object v1, v2
+  000b: const/4 v2, #int 2 // #2
+  000c: move v0, v2
+  000d: goto 0008 // -0005
+  catches
+    tries:
+      try 0002..0005
+      catch java.lang.RuntimeException -> 0009
+Blort.exTest2:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: add-int/lit8 v0, v0, #int 1 // #01
+  0003: move v2, v0
+  0004: invoke-static {v2}, Blort.maybeThrow:(I)I
+  0007: move-result v2
+  0008: move v0, v2
+  0009: add-int/lit8 v0, v0, #int 1 // #01
+  000b: move v2, v0
+  000c: invoke-static {v2}, Blort.maybeThrow:(I)I
+  000f: move-result v2
+  0010: move v0, v2
+  0011: return v0
+  0012: move-exception v2
+  0013: move-object v1, v2
+  0014: const/4 v2, #int 1 // #1
+  0015: move v0, v2
+  0016: goto 0011 // -0005
+  0017: move-exception v2
+  0018: move-object v1, v2
+  0019: const/4 v2, #int 3 // #3
+  001a: move v0, v2
+  001b: goto 0011 // -000a
+  catches
+    tries:
+      try 0004..0007
+      catch java.lang.RuntimeException -> 0012
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0017
diff --git a/dx/tests/046-dex-exceptions/info.txt b/dx/tests/046-dex-exceptions/info.txt
new file mode 100644
index 0000000..a4bc065
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple simple cases of exception handling get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/046-dex-exceptions/run b/dx/tests/046-dex-exceptions/run
new file mode 100644
index 0000000..8c821b6
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals --dump-method=Blort.exTest'*' *.class
diff --git a/dx/tests/047-dex-wide-args/Blort.java b/dx/tests/047-dex-wide-args/Blort.java
new file mode 100644
index 0000000..e7fc9b5
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static long test1(int w, long x, int y, long z) {
+        return w + x + y + z;
+    }
+
+    public static long test2(long w, int x, long y, int z) {
+        return w + x + y + z;
+    }
+}
diff --git a/dx/tests/047-dex-wide-args/expected.txt b/dx/tests/047-dex-wide-args/expected.txt
new file mode 100644
index 0000000..a6badfd
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/expected.txt
@@ -0,0 +1,35 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(IJIJ)J:
+regs: 0010; ins: 0006; outs: 0000
+  0000: move v0, v10
+  0001: move-wide v1, v11
+  0002: move v3, v13
+  0003: move-wide v4, v14
+  0004: move v6, v0
+  0005: int-to-long v6, v6
+  0006: move-wide v8, v1
+  0007: add-long/2addr v6, v8
+  0008: move v8, v3
+  0009: int-to-long v8, v8
+  000a: add-long/2addr v6, v8
+  000b: move-wide v8, v4
+  000c: add-long/2addr v6, v8
+  000d: move-wide v0, v6
+  000e: return-wide v0
+Blort.test2:(JIJI)J:
+regs: 0010; ins: 0006; outs: 0000
+  0000: move-wide v0, v10
+  0001: move v2, v12
+  0002: move-wide v3, v13
+  0003: move v5, v15
+  0004: move-wide v6, v0
+  0005: move v8, v2
+  0006: int-to-long v8, v8
+  0007: add-long/2addr v6, v8
+  0008: move-wide v8, v3
+  0009: add-long/2addr v6, v8
+  000a: move v8, v5
+  000b: int-to-long v8, v8
+  000c: add-long/2addr v6, v8
+  000d: move-wide v0, v6
+  000e: return-wide v0
diff --git a/dx/tests/047-dex-wide-args/info.txt b/dx/tests/047-dex-wide-args/info.txt
new file mode 100644
index 0000000..357204d
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+wide (category-2) arguments get treated reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/047-dex-wide-args/run b/dx/tests/047-dex-wide-args/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/048-dex-new-array/Blort.java b/dx/tests/048-dex-new-array/Blort.java
new file mode 100644
index 0000000..312370c
--- /dev/null
+++ b/dx/tests/048-dex-new-array/Blort.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void sink(Object x) {
+        // Do nothing.
+    }
+    
+    public static void test() {
+        sink(new boolean[0]);
+        sink(new byte[1]);
+        sink(new char[2]);
+        sink(new short[3]);
+        sink(new int[4]);
+        sink(new long[5]);
+        sink(new float[6]);
+        sink(new double[7]);
+        sink(new Object[0]);
+    }
+}
diff --git a/dx/tests/048-dex-new-array/expected.txt b/dx/tests/048-dex-new-array/expected.txt
new file mode 100644
index 0000000..6866d1f
--- /dev/null
+++ b/dx/tests/048-dex-new-array/expected.txt
@@ -0,0 +1,30 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v1, #int 0 // #0
+  0001: new-array v0, v1, boolean[]
+  0003: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0006: const/4 v0, #int 1 // #1
+  0007: new-array v0, v0, byte[]
+  0009: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  000c: const/4 v0, #int 2 // #2
+  000d: new-array v0, v0, char[]
+  000f: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0012: const/4 v0, #int 3 // #3
+  0013: new-array v0, v0, short[]
+  0015: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0018: const/4 v0, #int 4 // #4
+  0019: new-array v0, v0, int[]
+  001b: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  001e: const/4 v0, #int 5 // #5
+  001f: new-array v0, v0, long[]
+  0021: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0024: const/4 v0, #int 6 // #6
+  0025: new-array v0, v0, float[]
+  0027: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  002a: const/4 v0, #int 7 // #7
+  002b: new-array v0, v0, double[]
+  002d: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0030: new-array v0, v1, java.lang.Object[]
+  0032: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0035: return-void
diff --git a/dx/tests/048-dex-new-array/info.txt b/dx/tests/048-dex-new-array/info.txt
new file mode 100644
index 0000000..a2ff173
--- /dev/null
+++ b/dx/tests/048-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple array construction expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/048-dex-new-array/run b/dx/tests/048-dex-new-array/run
new file mode 100644
index 0000000..9927f1a
--- /dev/null
+++ b/dx/tests/048-dex-new-array/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test *.class
diff --git a/dx/tests/049-dex-instanceof/Blort.java b/dx/tests/049-dex-instanceof/Blort.java
new file mode 100644
index 0000000..2d46cd1
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static boolean test(Object x) {
+        return x instanceof Blort;
+    }
+}
diff --git a/dx/tests/049-dex-instanceof/expected.txt b/dx/tests/049-dex-instanceof/expected.txt
new file mode 100644
index 0000000..c8fa605
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/expected.txt
@@ -0,0 +1,8 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(Ljava/lang/Object;)Z:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: instance-of v1, v1, Blort
+  0004: move v0, v1
+  0005: return v0
diff --git a/dx/tests/049-dex-instanceof/info.txt b/dx/tests/049-dex-instanceof/info.txt
new file mode 100644
index 0000000..a6c82b2
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instanceof expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/049-dex-instanceof/run b/dx/tests/049-dex-instanceof/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/050-dex-checkcast/Blort.java b/dx/tests/050-dex-checkcast/Blort.java
new file mode 100644
index 0000000..893c3a3
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static Blort test(Object x) {
+        return (Blort) x;
+    }
+}
diff --git a/dx/tests/050-dex-checkcast/expected.txt b/dx/tests/050-dex-checkcast/expected.txt
new file mode 100644
index 0000000..270b03a
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/expected.txt
@@ -0,0 +1,8 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(Ljava/lang/Object;)LBlort;:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: check-cast v1, Blort
+  0004: move-object v0, v1
+  0005: return-object v0
diff --git a/dx/tests/050-dex-checkcast/info.txt b/dx/tests/050-dex-checkcast/info.txt
new file mode 100644
index 0000000..b23daa4
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+checked cast expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/050-dex-checkcast/run b/dx/tests/050-dex-checkcast/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/051-dex-explicit-null/Blort.java b/dx/tests/051-dex-explicit-null/Blort.java
new file mode 100644
index 0000000..f3bb333
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static Object test1() {
+        return null;
+    }
+
+    public static void test2(Object x) {
+        x = null;
+    }
+}
diff --git a/dx/tests/051-dex-explicit-null/expected.txt b/dx/tests/051-dex-explicit-null/expected.txt
new file mode 100644
index 0000000..8fb700c
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/expected.txt
@@ -0,0 +1,11 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()Ljava/lang/Object;:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: return-object v0
+Blort.test2:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #null // #0
+  0002: move-object v0, v1
+  0003: return-void
diff --git a/dx/tests/051-dex-explicit-null/info.txt b/dx/tests/051-dex-explicit-null/info.txt
new file mode 100644
index 0000000..8a03dc3
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+assigning a reference variable to null works, as well as explicitly
+returning a null.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/051-dex-explicit-null/run b/dx/tests/051-dex-explicit-null/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/052-dex-static-var-access/Blort.java b/dx/tests/052-dex-static-var-access/Blort.java
new file mode 100644
index 0000000..fdbefb4
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static boolean staticBoolean;
+    public static byte staticByte;
+    public static char staticChar;
+    public static short staticShort;
+    public static int staticInt;
+    public static long staticLong;
+    public static float staticFloat;
+    public static double staticDouble;
+    public static Object staticObject;
+
+    public static Object test1() {
+        int x = staticByte + staticChar + staticShort + staticInt +
+            (int) staticLong + (int) staticFloat + (int) staticDouble;
+
+        if (staticBoolean && (x > 0)) {;
+            return staticObject;
+        } else {
+            return null;
+        }
+    }
+
+    public static void test2(boolean b, int i, Object o) {
+        staticBoolean = b;
+        staticByte = (byte) i;
+        staticChar = (char) i;
+        staticShort = (short) i;
+        staticInt = i;
+        staticLong = i;
+        staticFloat = i;
+        staticDouble = i;
+        staticObject = o;
+    }
+}
diff --git a/dx/tests/052-dex-static-var-access/expected.txt b/dx/tests/052-dex-static-var-access/expected.txt
new file mode 100644
index 0000000..bfa9196
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/expected.txt
@@ -0,0 +1,60 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0000
+  0000: sget-byte v1, Blort.staticByte:B
+  0002: sget-char v2, Blort.staticChar:C
+  0004: add-int/2addr v1, v2
+  0005: sget-short v2, Blort.staticShort:S
+  0007: add-int/2addr v1, v2
+  0008: sget v2, Blort.staticInt:I
+  000a: add-int/2addr v1, v2
+  000b: sget-wide v2, Blort.staticLong:J
+  000d: long-to-int v2, v2
+  000e: add-int/2addr v1, v2
+  000f: sget v2, Blort.staticFloat:F
+  0011: float-to-int v2, v2
+  0012: add-int/2addr v1, v2
+  0013: sget-wide v2, Blort.staticDouble:D
+  0015: double-to-int v2, v2
+  0016: add-int/2addr v1, v2
+  0017: move v0, v1
+  0018: sget-boolean v1, Blort.staticBoolean:Z
+  001a: if-eqz v1, 0023 // +0009
+  001c: move v1, v0
+  001d: if-lez v1, 0023 // +0006
+  001f: sget-object v1, Blort.staticObject:Ljava/lang/Object;
+  0021: move-object v0, v1
+  0022: return-object v0
+  0023: const/4 v1, #null // #0
+  0024: move-object v0, v1
+  0025: goto 0022 // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 0008; ins: 0003; outs: 0000
+  0000: move v0, v5
+  0001: move v1, v6
+  0002: move-object v2, v7
+  0003: move v3, v0
+  0004: sput-boolean v3, Blort.staticBoolean:Z
+  0006: move v3, v1
+  0007: int-to-byte v3, v3
+  0008: sput-byte v3, Blort.staticByte:B
+  000a: move v3, v1
+  000b: int-to-char v3, v3
+  000c: sput-char v3, Blort.staticChar:C
+  000e: move v3, v1
+  000f: int-to-short v3, v3
+  0010: sput-short v3, Blort.staticShort:S
+  0012: move v3, v1
+  0013: sput v3, Blort.staticInt:I
+  0015: move v3, v1
+  0016: int-to-long v3, v3
+  0017: sput-wide v3, Blort.staticLong:J
+  0019: move v3, v1
+  001a: int-to-float v3, v3
+  001b: sput v3, Blort.staticFloat:F
+  001d: move v3, v1
+  001e: int-to-double v3, v3
+  001f: sput-wide v3, Blort.staticDouble:D
+  0021: move-object v3, v2
+  0022: sput-object v3, Blort.staticObject:Ljava/lang/Object;
+  0024: return-void
diff --git a/dx/tests/052-dex-static-var-access/info.txt b/dx/tests/052-dex-static-var-access/info.txt
new file mode 100644
index 0000000..c3c3b0b
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+static variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/052-dex-static-var-access/run b/dx/tests/052-dex-static-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/053-dex-instance-var-access/Blort.java b/dx/tests/053-dex-instance-var-access/Blort.java
new file mode 100644
index 0000000..1f68737
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public boolean insBoolean;
+    public byte insByte;
+    public char insChar;
+    public short insShort;
+    public int insInt;
+    public long insLong;
+    public float insFloat;
+    public double insDouble;
+    public Object insObject;
+
+    public Object test1() {
+        int x = insByte + insChar + insShort + insInt +
+            (int) insLong + (int) insFloat + (int) insDouble;
+
+        if (insBoolean && (x > 0)) {;
+            return insObject;
+        } else {
+            return null;
+        }
+    }
+
+    public void test2(boolean b, int i, Object o) {
+        insBoolean = b;
+        insByte = (byte) i;
+        insChar = (char) i;
+        insShort = (short) i;
+        insInt = i;
+        insLong = i;
+        insFloat = i;
+        insDouble = i;
+        insObject = o;
+    }
+}
diff --git a/dx/tests/053-dex-instance-var-access/expected.txt b/dx/tests/053-dex-instance-var-access/expected.txt
new file mode 100644
index 0000000..a0c0aca
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/expected.txt
@@ -0,0 +1,80 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()Ljava/lang/Object;:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: iget-byte v2, v2, Blort.insByte:B
+  0004: move-object v3, v0
+  0005: iget-char v3, v3, Blort.insChar:C
+  0007: add-int/2addr v2, v3
+  0008: move-object v3, v0
+  0009: iget-short v3, v3, Blort.insShort:S
+  000b: add-int/2addr v2, v3
+  000c: move-object v3, v0
+  000d: iget v3, v3, Blort.insInt:I
+  000f: add-int/2addr v2, v3
+  0010: move-object v3, v0
+  0011: iget-wide v3, v3, Blort.insLong:J
+  0013: long-to-int v3, v3
+  0014: add-int/2addr v2, v3
+  0015: move-object v3, v0
+  0016: iget v3, v3, Blort.insFloat:F
+  0018: float-to-int v3, v3
+  0019: add-int/2addr v2, v3
+  001a: move-object v3, v0
+  001b: iget-wide v3, v3, Blort.insDouble:D
+  001d: double-to-int v3, v3
+  001e: add-int/2addr v2, v3
+  001f: move v1, v2
+  0020: move-object v2, v0
+  0021: iget-boolean v2, v2, Blort.insBoolean:Z
+  0023: if-eqz v2, 002d // +000a
+  0025: move v2, v1
+  0026: if-lez v2, 002d // +0007
+  0028: move-object v2, v0
+  0029: iget-object v2, v2, Blort.insObject:Ljava/lang/Object;
+  002b: move-object v0, v2
+  002c: return-object v0
+  002d: const/4 v2, #null // #0
+  002e: move-object v0, v2
+  002f: goto 002c // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 000b; ins: 0004; outs: 0000
+  0000: move-object v0, v7
+  0001: move v1, v8
+  0002: move v2, v9
+  0003: move-object v3, v10
+  0004: move-object v4, v0
+  0005: move v5, v1
+  0006: iput-boolean v5, v4, Blort.insBoolean:Z
+  0008: move-object v4, v0
+  0009: move v5, v2
+  000a: int-to-byte v5, v5
+  000b: iput-byte v5, v4, Blort.insByte:B
+  000d: move-object v4, v0
+  000e: move v5, v2
+  000f: int-to-char v5, v5
+  0010: iput-char v5, v4, Blort.insChar:C
+  0012: move-object v4, v0
+  0013: move v5, v2
+  0014: int-to-short v5, v5
+  0015: iput-short v5, v4, Blort.insShort:S
+  0017: move-object v4, v0
+  0018: move v5, v2
+  0019: iput v5, v4, Blort.insInt:I
+  001b: move-object v4, v0
+  001c: move v5, v2
+  001d: int-to-long v5, v5
+  001e: iput-wide v5, v4, Blort.insLong:J
+  0020: move-object v4, v0
+  0021: move v5, v2
+  0022: int-to-float v5, v5
+  0023: iput v5, v4, Blort.insFloat:F
+  0025: move-object v4, v0
+  0026: move v5, v2
+  0027: int-to-double v5, v5
+  0028: iput-wide v5, v4, Blort.insDouble:D
+  002a: move-object v4, v0
+  002b: move-object v5, v3
+  002c: iput-object v5, v4, Blort.insObject:Ljava/lang/Object;
+  002e: return-void
diff --git a/dx/tests/053-dex-instance-var-access/info.txt b/dx/tests/053-dex-instance-var-access/info.txt
new file mode 100644
index 0000000..4f95797
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instance variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/053-dex-instance-var-access/run b/dx/tests/053-dex-instance-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/054-dex-high16/Blort.java b/dx/tests/054-dex-high16/Blort.java
new file mode 100644
index 0000000..e8976fa
--- /dev/null
+++ b/dx/tests/054-dex-high16/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void sink(int i) {
+        // Do nothing.
+    }
+
+    public static void sink(long l) {
+        // Do nothing.
+    }
+
+    public static void sink(float f) {
+        // Do nothing.
+    }
+
+    public static void sink(double d) {
+        // Do nothing.
+    }
+
+    public static void testInt() {
+        sink(Integer.MIN_VALUE);
+        sink(0x40000000);
+        sink(0x20000000);
+        sink(0x10000000);
+        sink(0x00080000);
+        sink(0x00040000);
+        sink(0x00020000);
+        sink(0x00010000);
+        sink(0x56780000);
+    }
+
+    public static void testLong() {
+        sink(Long.MIN_VALUE);
+        sink(0x4000000000000000L);
+        sink(0x2000000000000000L);
+        sink(0x1000000000000000L);
+        sink(0x0008000000000000L);
+        sink(0x0004000000000000L);
+        sink(0x0002000000000000L);
+        sink(0x0001000000000000L);
+        sink(0x5678000000000000L);
+    }
+
+    public static void testFloat() {
+        sink(Float.NEGATIVE_INFINITY);
+        sink(-0.0f);
+        sink(1.0f);
+        sink(Float.POSITIVE_INFINITY);
+        sink(Float.NaN);
+    }
+
+    public static void testDouble() {
+        sink(Double.NEGATIVE_INFINITY);
+        sink(-0.0);
+        sink(1.0);
+        sink(Double.POSITIVE_INFINITY);
+        sink(Double.NaN);
+    }
+}
diff --git a/dx/tests/054-dex-high16/expected.txt b/dx/tests/054-dex-high16/expected.txt
new file mode 100644
index 0000000..42902bc
--- /dev/null
+++ b/dx/tests/054-dex-high16/expected.txt
@@ -0,0 +1,69 @@
+javac 1.7.0-internal_bootstrap
+Blort.testDouble:()V:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const-wide/high16 v0, #double -Infinity // #fff0000000000000
+  0002: invoke-static {v0, v1}, Blort.sink:(D)V
+  0005: const-wide/high16 v0, #double -0.0 // #8000000000000000
+  0007: invoke-static {v0, v1}, Blort.sink:(D)V
+  000a: const-wide/high16 v0, #double 1.0 // #3ff0000000000000
+  000c: invoke-static {v0, v1}, Blort.sink:(D)V
+  000f: const-wide/high16 v0, #double Infinity // #7ff0000000000000
+  0011: invoke-static {v0, v1}, Blort.sink:(D)V
+  0014: const-wide/high16 v0, #double NaN // #7ff8000000000000
+  0016: invoke-static {v0, v1}, Blort.sink:(D)V
+  0019: return-void
+Blort.testFloat:()V:
+regs: 0001; ins: 0000; outs: 0001
+  0000: const/high16 v0, #float -Infinity // #ff800000
+  0002: invoke-static {v0}, Blort.sink:(F)V
+  0005: const/high16 v0, #float -0.0 // #80000000
+  0007: invoke-static {v0}, Blort.sink:(F)V
+  000a: const/high16 v0, #float 1.0 // #3f800000
+  000c: invoke-static {v0}, Blort.sink:(F)V
+  000f: const/high16 v0, #float Infinity // #7f800000
+  0011: invoke-static {v0}, Blort.sink:(F)V
+  0014: const/high16 v0, #float NaN // #7fc00000
+  0016: invoke-static {v0}, Blort.sink:(F)V
+  0019: return-void
+Blort.testInt:()V:
+regs: 0001; ins: 0000; outs: 0001
+  0000: const/high16 v0, #int -2147483648 // #80000000
+  0002: invoke-static {v0}, Blort.sink:(I)V
+  0005: const/high16 v0, #int 1073741824 // #40000000
+  0007: invoke-static {v0}, Blort.sink:(I)V
+  000a: const/high16 v0, #int 536870912 // #20000000
+  000c: invoke-static {v0}, Blort.sink:(I)V
+  000f: const/high16 v0, #int 268435456 // #10000000
+  0011: invoke-static {v0}, Blort.sink:(I)V
+  0014: const/high16 v0, #int 524288 // #00080000
+  0016: invoke-static {v0}, Blort.sink:(I)V
+  0019: const/high16 v0, #int 262144 // #00040000
+  001b: invoke-static {v0}, Blort.sink:(I)V
+  001e: const/high16 v0, #int 131072 // #00020000
+  0020: invoke-static {v0}, Blort.sink:(I)V
+  0023: const/high16 v0, #int 65536 // #00010000
+  0025: invoke-static {v0}, Blort.sink:(I)V
+  0028: const/high16 v0, #int 1450704896 // #56780000
+  002a: invoke-static {v0}, Blort.sink:(I)V
+  002d: return-void
+Blort.testLong:()V:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const-wide/high16 v0, #long -9223372036854775808 // #8000000000000000
+  0002: invoke-static {v0, v1}, Blort.sink:(J)V
+  0005: const-wide/high16 v0, #long 4611686018427387904 // #4000000000000000
+  0007: invoke-static {v0, v1}, Blort.sink:(J)V
+  000a: const-wide/high16 v0, #long 2305843009213693952 // #2000000000000000
+  000c: invoke-static {v0, v1}, Blort.sink:(J)V
+  000f: const-wide/high16 v0, #long 1152921504606846976 // #1000000000000000
+  0011: invoke-static {v0, v1}, Blort.sink:(J)V
+  0014: const-wide/high16 v0, #long 2251799813685248 // #0008000000000000
+  0016: invoke-static {v0, v1}, Blort.sink:(J)V
+  0019: const-wide/high16 v0, #long 1125899906842624 // #0004000000000000
+  001b: invoke-static {v0, v1}, Blort.sink:(J)V
+  001e: const-wide/high16 v0, #long 562949953421312 // #0002000000000000
+  0020: invoke-static {v0, v1}, Blort.sink:(J)V
+  0023: const-wide/high16 v0, #long 281474976710656 // #0001000000000000
+  0025: invoke-static {v0, v1}, Blort.sink:(J)V
+  0028: const-wide/high16 v0, #long 6230730084467081216 // #5678000000000000
+  002a: invoke-static {v0, v1}, Blort.sink:(J)V
+  002d: return-void
diff --git a/dx/tests/054-dex-high16/info.txt b/dx/tests/054-dex-high16/info.txt
new file mode 100644
index 0000000..ef1fac4
--- /dev/null
+++ b/dx/tests/054-dex-high16/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+"high16" constants get converted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/054-dex-high16/run b/dx/tests/054-dex-high16/run
new file mode 100644
index 0000000..a2c7458
--- /dev/null
+++ b/dx/tests/054-dex-high16/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/055-dex-explicit-throw/Blort.java b/dx/tests/055-dex-explicit-throw/Blort.java
new file mode 100644
index 0000000..a47ba90
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    private static RuntimeException theException = new RuntimeException();
+
+    public static void test1() {
+        throw theException;
+    }
+
+    public static int test2() {
+        try {
+            throw theException;
+        } catch (RuntimeException ex) {
+            return 1;
+        }
+    }
+}
diff --git a/dx/tests/055-dex-explicit-throw/expected.txt b/dx/tests/055-dex-explicit-throw/expected.txt
new file mode 100644
index 0000000..1c62431
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/expected.txt
@@ -0,0 +1,18 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: sget-object v0, Blort.theException:Ljava/lang/RuntimeException;
+  0002: throw v0
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: sget-object v1, Blort.theException:Ljava/lang/RuntimeException;
+  0002: throw v1
+  0003: move-exception v1
+  0004: move-object v0, v1
+  0005: const/4 v1, #int 1 // #1
+  0006: move v0, v1
+  0007: return v0
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0003
diff --git a/dx/tests/055-dex-explicit-throw/info.txt b/dx/tests/055-dex-explicit-throw/info.txt
new file mode 100644
index 0000000..b3b7808
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+explicit use of "throw" gets converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/055-dex-explicit-throw/run b/dx/tests/055-dex-explicit-throw/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/056-dex-call-interface/Blort.java b/dx/tests/056-dex-call-interface/Blort.java
new file mode 100644
index 0000000..75775e6
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test(Zorch z, double d) {
+        z.zorch1();
+        z.zorch2(d);
+        int x = z.zorch3(z);
+        int y = (int) z.zorch4();
+        return x + y;
+    }
+}
diff --git a/dx/tests/056-dex-call-interface/Zorch.java b/dx/tests/056-dex-call-interface/Zorch.java
new file mode 100644
index 0000000..03e3762
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Zorch.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface Zorch
+{
+    public void zorch1();
+    public void zorch2(double d);
+    public int zorch3(Object o);
+    public long zorch4();
+}
diff --git a/dx/tests/056-dex-call-interface/expected.txt b/dx/tests/056-dex-call-interface/expected.txt
new file mode 100644
index 0000000..d3e7451
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/expected.txt
@@ -0,0 +1,25 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(LZorch;D)I:
+regs: 000b; ins: 0003; outs: 0003
+  0000: move-object v0, v8
+  0001: move-wide v1, v9
+  0002: move-object v5, v0
+  0003: invoke-interface {v5}, Zorch.zorch1:()V
+  0006: move-object v5, v0
+  0007: move-wide v6, v1
+  0008: invoke-interface {v5, v6, v7}, Zorch.zorch2:(D)V
+  000b: move-object v5, v0
+  000c: move-object v6, v0
+  000d: invoke-interface {v5, v6}, Zorch.zorch3:(Ljava/lang/Object;)I
+  0010: move-result v5
+  0011: move v3, v5
+  0012: move-object v5, v0
+  0013: invoke-interface {v5}, Zorch.zorch4:()J
+  0016: move-result-wide v5
+  0017: long-to-int v5, v5
+  0018: move v4, v5
+  0019: move v5, v3
+  001a: move v6, v4
+  001b: add-int/2addr v5, v6
+  001c: move v0, v5
+  001d: return v0
diff --git a/dx/tests/056-dex-call-interface/info.txt b/dx/tests/056-dex-call-interface/info.txt
new file mode 100644
index 0000000..2f964a2
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of interface method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/056-dex-call-interface/run b/dx/tests/056-dex-call-interface/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/057-dex-call-virtual/Blort.java b/dx/tests/057-dex-call-virtual/Blort.java
new file mode 100644
index 0000000..e32135b
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test(Zorch z) {
+        z.zorch1();
+        return z.zorch2(5);
+    }
+}
diff --git a/dx/tests/057-dex-call-virtual/Zorch.java b/dx/tests/057-dex-call-virtual/Zorch.java
new file mode 100644
index 0000000..718601f
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+    public void zorch1() { 
+        // This space intentionally left blank.
+    }
+
+    public int zorch2(int x) {
+        return 0;
+    }
+}
diff --git a/dx/tests/057-dex-call-virtual/expected.txt b/dx/tests/057-dex-call-virtual/expected.txt
new file mode 100644
index 0000000..d4932cc
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/expected.txt
@@ -0,0 +1,12 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(LZorch;)I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-virtual {v1}, Zorch.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-virtual {v1, v2}, Zorch.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/057-dex-call-virtual/info.txt b/dx/tests/057-dex-call-virtual/info.txt
new file mode 100644
index 0000000..452c9cb
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of regular virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/057-dex-call-virtual/run b/dx/tests/057-dex-call-virtual/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/058-dex-call-direct/Blort.java b/dx/tests/058-dex-call-direct/Blort.java
new file mode 100644
index 0000000..77e224c
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/Blort.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test(Blort b) {
+        b.zorch1();
+        return b.zorch2(5);
+    }
+
+    private void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    private int zorch2(int x) {
+        return 1;
+    }
+}
diff --git a/dx/tests/058-dex-call-direct/expected.txt b/dx/tests/058-dex-call-direct/expected.txt
new file mode 100644
index 0000000..cac99b1
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/expected.txt
@@ -0,0 +1,12 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(LBlort;)I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-direct {v1}, Blort.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-direct {v1, v2}, Blort.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/058-dex-call-direct/info.txt b/dx/tests/058-dex-call-direct/info.txt
new file mode 100644
index 0000000..a22c479
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of direct instance method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/058-dex-call-direct/run b/dx/tests/058-dex-call-direct/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/059-dex-call-super/Blort.java b/dx/tests/059-dex-call-super/Blort.java
new file mode 100644
index 0000000..599440b
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+    extends Zorch
+{
+    public int test1() {
+        super.zorch1();
+        return super.zorch2(5);
+    }
+
+    public void test2() {
+        super.test2();
+    }
+}
diff --git a/dx/tests/059-dex-call-super/Zorch.java b/dx/tests/059-dex-call-super/Zorch.java
new file mode 100644
index 0000000..2f8951a
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Zorch.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+    public void zorch1() { 
+        // This space intentionally left blank.
+    }
+
+    public int zorch2(int x) {
+        return 0;
+    }
+
+    public void test2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/059-dex-call-super/expected.txt b/dx/tests/059-dex-call-super/expected.txt
new file mode 100644
index 0000000..e18c9df
--- /dev/null
+++ b/dx/tests/059-dex-call-super/expected.txt
@@ -0,0 +1,18 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-super {v1}, Zorch.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-super {v1, v2}, Zorch.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
+Blort.test2:()V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: invoke-super {v1}, Zorch.test2:()V
+  0005: return-void
diff --git a/dx/tests/059-dex-call-super/info.txt b/dx/tests/059-dex-call-super/info.txt
new file mode 100644
index 0000000..ff88814
--- /dev/null
+++ b/dx/tests/059-dex-call-super/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of superclass virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/059-dex-call-super/run b/dx/tests/059-dex-call-super/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/059-dex-call-super/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/060-dex-call-static/Blort.java b/dx/tests/060-dex-call-static/Blort.java
new file mode 100644
index 0000000..2b9bb48
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test() {
+        Zorch.zorch1();
+        return Zorch.zorch2(5);
+    }
+}
diff --git a/dx/tests/060-dex-call-static/Zorch.java b/dx/tests/060-dex-call-static/Zorch.java
new file mode 100644
index 0000000..8ccc448
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+    public static void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    public static int zorch2(int x) {
+        return 1;
+    }
+}
diff --git a/dx/tests/060-dex-call-static/expected.txt b/dx/tests/060-dex-call-static/expected.txt
new file mode 100644
index 0000000..f451938
--- /dev/null
+++ b/dx/tests/060-dex-call-static/expected.txt
@@ -0,0 +1,8 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()I:
+regs: 0001; ins: 0000; outs: 0001
+  0000: invoke-static {}, Zorch.zorch1:()V
+  0003: const/4 v0, #int 5 // #5
+  0004: invoke-static {v0}, Zorch.zorch2:(I)I
+  0007: move-result v0
+  0008: return v0
diff --git a/dx/tests/060-dex-call-static/info.txt b/dx/tests/060-dex-call-static/info.txt
new file mode 100644
index 0000000..1253355
--- /dev/null
+++ b/dx/tests/060-dex-call-static/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of static method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/060-dex-call-static/run b/dx/tests/060-dex-call-static/run
new file mode 100644
index 0000000..346856c
--- /dev/null
+++ b/dx/tests/060-dex-call-static/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/061-dex-try-catch/Blort.java b/dx/tests/061-dex-try-catch/Blort.java
new file mode 100644
index 0000000..b63a41e
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void caught() {
+        // This space intentionally left blank.
+    }
+
+    public static void zorch(int x) {
+        // This space intentionally left blank.
+    }
+
+    public static void test1(int x) {
+        // In this test, the code being try-caught can't possibly throw.
+        try {
+            x = 0;
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test2(String[] sa) {
+        // In this test, the code being try-caught doesn't contain any
+        // constant pool references.
+        try {
+            int x = sa.length;
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test3() {
+        // In this test, the code being try-caught contains a constant
+        // pool reference.
+        try {
+            zorch(1);
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test4(String[] sa) {
+        // In this test, the code being try-caught contains one
+        // throwing instruction that has a constant pool reference and
+        // one that doesn't.
+        try {
+            zorch(sa.length);
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+}
diff --git a/dx/tests/061-dex-try-catch/expected.txt b/dx/tests/061-dex-try-catch/expected.txt
new file mode 100644
index 0000000..dc8e6a7
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/expected.txt
@@ -0,0 +1,50 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #int 0 // #0
+  0002: move v0, v2
+  0003: return-void
+Blort.test2:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: array-length v2, v2
+  0003: move v1, v2
+  0004: return-void
+  0005: move-exception v2
+  0006: move-object v1, v2
+  0007: invoke-static {}, Blort.caught:()V
+  000a: goto 0004 // -0006
+  catches
+    tries:
+      try 0002..0003
+      catch java.lang.RuntimeException -> 0005
+Blort.test3:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch:(I)V
+  0004: return-void
+  0005: move-exception v1
+  0006: move-object v0, v1
+  0007: invoke-static {}, Blort.caught:()V
+  000a: goto 0004 // -0006
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.RuntimeException -> 0005
+Blort.test4:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: array-length v2, v2
+  0003: invoke-static {v2}, Blort.zorch:(I)V
+  0006: return-void
+  0007: move-exception v2
+  0008: move-object v1, v2
+  0009: invoke-static {}, Blort.caught:()V
+  000c: goto 0006 // -0006
+  catches
+    tries:
+      try 0002..0006
+      catch java.lang.RuntimeException -> 0007
diff --git a/dx/tests/061-dex-try-catch/info.txt b/dx/tests/061-dex-try-catch/info.txt
new file mode 100644
index 0000000..409b251
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of try-catch work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/061-dex-try-catch/run b/dx/tests/061-dex-try-catch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/062-dex-synch-method/Blort.java b/dx/tests/062-dex-synch-method/Blort.java
new file mode 100644
index 0000000..643165d
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public synchronized void testInstance1() {
+        // This space intentionally left blank.
+    }
+
+    public synchronized void testInstance2(Object x) {
+        x.hashCode();
+    }
+
+    public synchronized int testInstance3(int x, int y, int z) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public synchronized long testInstance4(long x) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public static synchronized void testStatic1() {
+        // This space intentionally left blank.
+    }
+
+    public static synchronized void testStatic2(Object x) {
+        x.hashCode();
+    }
+
+    public static synchronized int testStatic3(int x, int y, int z) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public static synchronized long testStatic4(long x) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+}
diff --git a/dx/tests/062-dex-synch-method/expected.txt b/dx/tests/062-dex-synch-method/expected.txt
new file mode 100644
index 0000000..9492aa1
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/expected.txt
@@ -0,0 +1,117 @@
+javac 1.7.0-internal_bootstrap
+Blort.testInstance1:()V:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v2
+  0002: monitor-enter v1
+  0003: monitor-exit v1
+  0004: return-void
+Blort.testInstance2:(Ljava/lang/Object;)V:
+regs: 0006; ins: 0002; outs: 0001
+  0000: move-object v0, v4
+  0001: move-object v1, v5
+  0002: move-object v3, v4
+  0003: monitor-enter v3
+  0004: move-object v2, v1
+  0005: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  0008: move-result v2
+  0009: monitor-exit v3
+  000a: return-void
+  000b: move-exception v0
+  000c: monitor-exit v3
+  000d: throw v0
+  catches
+    tries:
+      try 0005..0008
+      catch <any> -> 000b
+Blort.testInstance3:(III)I:
+regs: 000b; ins: 0004; outs: 0000
+  0000: move-object v0, v7
+  0001: move v1, v8
+  0002: move v2, v9
+  0003: move v3, v10
+  0004: move-object v6, v7
+  0005: monitor-enter v6
+  0006: move v4, v1
+  0007: const/4 v5, #int 1 // #1
+  0008: if-ne v4, v5, 000e // +0006
+  000a: const/4 v4, #int 1 // #1
+  000b: move v0, v4
+  000c: monitor-exit v6
+  000d: return v0
+  000e: const/4 v4, #int 2 // #2
+  000f: move v0, v4
+  0010: goto 000c // -0004
+Blort.testInstance4:(J)J:
+regs: 000b; ins: 0003; outs: 0000
+  0000: move-object v0, v8
+  0001: move-wide v1, v9
+  0002: move-object v7, v8
+  0003: monitor-enter v7
+  0004: move-wide v3, v1
+  0005: const-wide/16 v5, #long 1 // #0001
+  0007: cmp-long v3, v3, v5
+  0009: if-nez v3, 0010 // +0007
+  000b: const-wide/16 v3, #long 1 // #0001
+  000d: move-wide v0, v3
+  000e: monitor-exit v7
+  000f: return-wide v0
+  0010: const-wide/16 v3, #long 2 // #0002
+  0012: move-wide v0, v3
+  0013: goto 000e // -0005
+Blort.testStatic1:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const-class v0, Blort
+  0002: monitor-enter v0
+  0003: monitor-exit v0
+  0004: return-void
+Blort.testStatic2:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move-object v0, v3
+  0001: const-class v2, Blort
+  0003: monitor-enter v2
+  0004: move-object v1, v0
+  0005: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+  0008: move-result v1
+  0009: monitor-exit v2
+  000a: return-void
+  000b: move-exception v0
+  000c: monitor-exit v2
+  000d: throw v0
+  catches
+    tries:
+      try 0005..0008
+      catch <any> -> 000b
+Blort.testStatic3:(III)I:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: const-class v5, Blort
+  0005: monitor-enter v5
+  0006: move v3, v0
+  0007: const/4 v4, #int 1 // #1
+  0008: if-ne v3, v4, 000e // +0006
+  000a: const/4 v3, #int 1 // #1
+  000b: move v0, v3
+  000c: monitor-exit v5
+  000d: return v0
+  000e: const/4 v3, #int 2 // #2
+  000f: move v0, v3
+  0010: goto 000c // -0004
+Blort.testStatic4:(J)J:
+regs: 0009; ins: 0002; outs: 0000
+  0000: move-wide v0, v7
+  0001: const-class v6, Blort
+  0003: monitor-enter v6
+  0004: move-wide v2, v0
+  0005: const-wide/16 v4, #long 1 // #0001
+  0007: cmp-long v2, v2, v4
+  0009: if-nez v2, 0010 // +0007
+  000b: const-wide/16 v2, #long 1 // #0001
+  000d: move-wide v0, v2
+  000e: monitor-exit v6
+  000f: return-wide v0
+  0010: const-wide/16 v2, #long 2 // #0002
+  0012: move-wide v0, v2
+  0013: goto 000e // -0005
diff --git a/dx/tests/062-dex-synch-method/info.txt b/dx/tests/062-dex-synch-method/info.txt
new file mode 100644
index 0000000..0e82727
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of synchronized methods get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/062-dex-synch-method/run b/dx/tests/062-dex-synch-method/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/063-dex-empty-switch/Blort.java b/dx/tests/063-dex-empty-switch/Blort.java
new file mode 100644
index 0000000..2d996f3
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public int test1(int x) {
+        switch (x) {
+            default: return 1;
+        }
+    }
+
+    public int test2(int x) {
+        switch (x) {
+            default: x = 1;
+        }
+
+        return x;
+    }
+}
diff --git a/dx/tests/063-dex-empty-switch/expected.txt b/dx/tests/063-dex-empty-switch/expected.txt
new file mode 100644
index 0000000..4e0f6ba
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/expected.txt
@@ -0,0 +1,19 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 1 // #1
+  0004: move v0, v2
+  0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 1 // #1
+  0004: move v1, v2
+  0005: move v2, v1
+  0006: move v0, v2
+  0007: return v0
diff --git a/dx/tests/063-dex-empty-switch/info.txt b/dx/tests/063-dex-empty-switch/info.txt
new file mode 100644
index 0000000..38f212b
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of empty (that is, default-only) switch statements
+get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/063-dex-empty-switch/run b/dx/tests/063-dex-empty-switch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/064-dex-array-access/Blort.java b/dx/tests/064-dex-array-access/Blort.java
new file mode 100644
index 0000000..3f1d1d8
--- /dev/null
+++ b/dx/tests/064-dex-array-access/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public boolean test01(boolean[] x) {
+        x[0] = true;
+        return x[1];
+    }
+
+    public byte test02(byte[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public short test03(short[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public char test04(char[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public int test05(int[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public long test06(long[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public float test07(float[] x) {
+        x[0] = 2.0f;
+        return x[1];
+    }
+
+    public double test08(double[] x) {
+        x[0] = 2.0;
+        return x[1];
+    }
+
+    public Object test09(Object[] x) {
+        x[0] = null;
+        return x[1];
+    }
+
+    public static Object test10(Object[][] x) {
+        x[0][0] = null;
+        return x[1][2];
+    }
+
+    public static int test11(Object x) {
+        int[][][] arr = (int[][][]) x;
+        arr[0][0][0] = 123;
+        return arr[1][2][3];
+    }
+}
diff --git a/dx/tests/064-dex-array-access/expected.txt b/dx/tests/064-dex-array-access/expected.txt
new file mode 100644
index 0000000..d1912f8
--- /dev/null
+++ b/dx/tests/064-dex-array-access/expected.txt
@@ -0,0 +1,158 @@
+javac 1.7.0-internal_bootstrap
+Blort.test01:([Z)Z:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 1 // #1
+  0005: aput-boolean v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-boolean v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test02:([B)B:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-byte v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-byte v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test03:([S)S:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-short v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-short v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test04:([C)C:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-char v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-char v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test05:([I)I:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test06:([J)J:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-object v0, v6
+  0001: move-object v1, v7
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const-wide/16 v4, #long 5 // #0005
+  0006: aput-wide v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget-wide v2, v2, v3
+  000c: move-wide v0, v2
+  000d: return-wide v0
+Blort.test07:([F)F:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/high16 v4, #float 2.0 // #40000000
+  0006: aput v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget v2, v2, v3
+  000c: move v0, v2
+  000d: return v0
+Blort.test08:([D)D:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-object v0, v6
+  0001: move-object v1, v7
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const-wide/high16 v4, #double 2.0 // #4000000000000000
+  0006: aput-wide v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget-wide v2, v2, v3
+  000c: move-wide v0, v2
+  000d: return-wide v0
+Blort.test09:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #null // #0
+  0005: aput-object v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-object v2, v2, v3
+  000b: move-object v0, v2
+  000c: return-object v0
+Blort.test10:([[Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move-object v0, v4
+  0001: move-object v1, v0
+  0002: const/4 v2, #int 0 // #0
+  0003: aget-object v1, v1, v2
+  0005: const/4 v2, #int 0 // #0
+  0006: const/4 v3, #null // #0
+  0007: aput-object v3, v1, v2
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 1 // #1
+  000b: aget-object v1, v1, v2
+  000d: const/4 v2, #int 2 // #2
+  000e: aget-object v1, v1, v2
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test11:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: check-cast v2, int[][][]
+  0004: check-cast v2, int[][][]
+  0006: move-object v1, v2
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 0 // #0
+  0009: aget-object v2, v2, v3
+  000b: const/4 v3, #int 0 // #0
+  000c: aget-object v2, v2, v3
+  000e: const/4 v3, #int 0 // #0
+  000f: const/16 v4, #int 123 // #007b
+  0011: aput v4, v2, v3
+  0013: move-object v2, v1
+  0014: const/4 v3, #int 1 // #1
+  0015: aget-object v2, v2, v3
+  0017: const/4 v3, #int 2 // #2
+  0018: aget-object v2, v2, v3
+  001a: const/4 v3, #int 3 // #3
+  001b: aget v2, v2, v3
+  001d: move v0, v2
+  001e: return v0
diff --git a/dx/tests/064-dex-array-access/info.txt b/dx/tests/064-dex-array-access/info.txt
new file mode 100644
index 0000000..8f81663
--- /dev/null
+++ b/dx/tests/064-dex-array-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array access get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/064-dex-array-access/run b/dx/tests/064-dex-array-access/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/064-dex-array-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/065-dex-new-array/Blort.java b/dx/tests/065-dex-new-array/Blort.java
new file mode 100644
index 0000000..93af87f
--- /dev/null
+++ b/dx/tests/065-dex-new-array/Blort.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public boolean[] test1() {
+        return new boolean[1];
+    }
+
+    public byte[] test2() {
+        return new byte[1];
+    }
+
+    public short[] test3() {
+        return new short[1];
+    }
+
+    public char[] test4() {
+        return new char[1];
+    }
+
+    public int[] test5() {
+        return new int[1];
+    }
+
+    public long[] test6() {
+        return new long[1];
+    }
+
+    public float[] test7() {
+        return new float[1];
+    }
+
+    public double[] test8() {
+        return new double[1];
+    }
+
+    public Object[] test9() {
+        return new Object[1];
+    }
+}
diff --git a/dx/tests/065-dex-new-array/expected.txt b/dx/tests/065-dex-new-array/expected.txt
new file mode 100644
index 0000000..a16ebe9
--- /dev/null
+++ b/dx/tests/065-dex-new-array/expected.txt
@@ -0,0 +1,64 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()[Z:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, boolean[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test2:()[B:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, byte[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test3:()[S:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, short[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test4:()[C:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, char[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test5:()[I:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, int[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test6:()[J:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, long[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test7:()[F:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, float[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test8:()[D:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, double[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test9:()[Ljava/lang/Object;:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, java.lang.Object[]
+  0004: move-object v0, v1
+  0005: return-object v0
diff --git a/dx/tests/065-dex-new-array/info.txt b/dx/tests/065-dex-new-array/info.txt
new file mode 100644
index 0000000..f4d2cc5
--- /dev/null
+++ b/dx/tests/065-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/065-dex-new-array/run b/dx/tests/065-dex-new-array/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/065-dex-new-array/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/066-dex-try-catch-rethrow/Blort.java b/dx/tests/066-dex-try-catch-rethrow/Blort.java
new file mode 100644
index 0000000..cefc0fd
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/Blort.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static Object zorch1(String s) {
+        return null;
+    }
+    
+    public static void test1() {
+        try {
+            zorch1("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static void zorch2(String s) {
+        // This space intentionally left blank.
+    }
+
+    public static void test2() {
+        try {
+            zorch2("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static int zorch3(String s) {
+        return 0;
+    }
+    
+    public static void test3() {
+        try {
+            zorch3("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static Object zorch4(int x) {
+        return null;
+    }
+    
+    public static void test4() {
+        try {
+            zorch4(1);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static Object zorch5(int x) {
+        return null;
+    }
+    
+    public static Object test5() {
+        try {
+            return zorch5(1);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/tests/066-dex-try-catch-rethrow/expected.txt b/dx/tests/066-dex-try-catch-rethrow/expected.txt
new file mode 100644
index 0000000..3711463
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/expected.txt
@@ -0,0 +1,96 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch1:(Ljava/lang/String;)Ljava/lang/Object;
+  0005: move-result-object v1
+  0006: return-void
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0007
+Blort.test2:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch2:(Ljava/lang/String;)V
+  0005: return-void
+  0006: move-exception v1
+  0007: move-object v0, v1
+  0008: new-instance v1, java.lang.RuntimeException
+  000a: move-object v4, v1
+  000b: move-object v1, v4
+  000c: move-object v2, v4
+  000d: move-object v3, v0
+  000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0011: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0006
+Blort.test3:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch3:(Ljava/lang/String;)I
+  0005: move-result v1
+  0006: return-void
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0007
+Blort.test4:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch4:(I)Ljava/lang/Object;
+  0004: move-result-object v1
+  0005: return-void
+  0006: move-exception v1
+  0007: move-object v0, v1
+  0008: new-instance v1, java.lang.RuntimeException
+  000a: move-object v4, v1
+  000b: move-object v1, v4
+  000c: move-object v2, v4
+  000d: move-object v3, v0
+  000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0011: throw v1
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.Exception -> 0006
+Blort.test5:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch5:(I)Ljava/lang/Object;
+  0004: move-result-object v1
+  0005: move-object v0, v1
+  0006: return-object v0
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.Exception -> 0007
diff --git a/dx/tests/066-dex-try-catch-rethrow/info.txt b/dx/tests/066-dex-try-catch-rethrow/info.txt
new file mode 100644
index 0000000..b2696b8
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which looks at a few cases of
+a try-catch where the exception handler rethrows.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/066-dex-try-catch-rethrow/run b/dx/tests/066-dex-try-catch-rethrow/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/067-dex-switch-and-try/Blort.java b/dx/tests/067-dex-switch-and-try/Blort.java
new file mode 100644
index 0000000..d90bd32
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/Blort.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    static public void blort() {
+        // This space intentionally left blank.
+    }
+
+    // This test has a try-catch but the try code can't possibly throw.
+    public int test1(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    x = 10;
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            // Ignore it.
+        }
+
+        return x;
+    }
+
+    // This test has a try-catch where the try code can theoretically throw.
+    public int test2(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    x = 10;
+                    blort();
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            // Ignore it.
+        }
+
+        return x;
+    }
+
+    // This test has a switch with a case that has a try-catch where
+    // the try code can theoretically throw, but it would be caught
+    // inside the case itself.
+    public int test3(int x) {
+        switch (x) {
+            case 1: {
+                try {
+                    x = 10;
+                    blort();
+                } catch (RuntimeException ex) {
+                    // Ignore it.
+                }
+                break;
+            }
+            case 2: {
+                x = 20;
+                break;
+            }
+        }
+
+        return x;
+    }
+
+    // This test has a try-catch that has a switch with a case that
+    // has a try-catch where the try code can theoretically throw, but
+    // it would be caught inside the case itself, so the outer
+    // exception handler should be considered dead.
+    public int test4(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    try {
+                        x = 10;
+                        blort();
+                    } catch (RuntimeException ex) {
+                        // Ignore it.
+                    }
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            return 4;
+        }
+
+        return x;
+    }
+}
diff --git a/dx/tests/067-dex-switch-and-try/expected.txt b/dx/tests/067-dex-switch-and-try/expected.txt
new file mode 100644
index 0000000..4381710
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/expected.txt
@@ -0,0 +1,101 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0012 // +000f
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: goto 0006 // -0006
+  000d: const/16 v3, #int 20 // #0014
+  000f: move v1, v3
+  0010: goto 0006 // -000a
+  0011: nop // spacer
+  0012: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 0000000d // +0000000a
+Blort.test2:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: const/16 v3, #int 20 // #0014
+  0012: move v1, v3
+  0013: goto 0006 // -000d
+  0014: move-exception v3
+  0015: move-object v2, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000010 // +0000000d
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0014
+Blort.test3:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: move-exception v3
+  0011: move-object v2, v3
+  0012: goto 0006 // -000c
+  0013: const/16 v3, #int 20 // #0014
+  0015: move v1, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000013 // +00000010
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0010
+Blort.test4:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: move-exception v3
+  0011: move-object v2, v3
+  0012: goto 0006 // -000c
+  0013: const/16 v3, #int 20 // #0014
+  0015: move v1, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000013 // +00000010
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0010
diff --git a/dx/tests/067-dex-switch-and-try/info.txt b/dx/tests/067-dex-switch-and-try/info.txt
new file mode 100644
index 0000000..68e8117
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which looks at a couple cases of
+embedding a switch statement in a try-catch and vice versa. This test
+was created specifically because of a bug with exactly this situation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/067-dex-switch-and-try/run b/dx/tests/067-dex-switch-and-try/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/068-dex-infinite-loop/Blort.java b/dx/tests/068-dex-infinite-loop/Blort.java
new file mode 100644
index 0000000..09c45a4
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/Blort.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static boolean zorch() {
+        return true;
+    }
+    
+    public static void test1() {
+        for (;;) {
+            // This space intentionally left blank.
+        }
+    }
+
+    public static void test2() {
+        while (zorch()) {
+            // This space intentionally left blank.
+        }
+    }
+
+    public static void test3() {
+        while (zorch()) {
+            zorch();
+        }
+    }
+
+    public static void test4() {
+        for (;;) {
+            if (zorch()) {
+                break;
+            }
+            
+            while (zorch()) {
+                zorch();
+            }
+        }
+    }
+}
diff --git a/dx/tests/068-dex-infinite-loop/expected.txt b/dx/tests/068-dex-infinite-loop/expected.txt
new file mode 100644
index 0000000..6f3064d
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/expected.txt
@@ -0,0 +1,29 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()V:
+regs: 0000; ins: 0000; outs: 0000
+  0000: goto/32 0000 // +0000
+Blort.test2:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-nez v0, 0000 // -0004
+  0006: return-void
+Blort.test3:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-eqz v0, 000a // +0006
+  0006: invoke-static {}, Blort.zorch:()Z
+  0009: goto 0000 // -0009
+  000a: return-void
+Blort.test4:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-eqz v0, 0007 // +0003
+  0006: return-void
+  0007: invoke-static {}, Blort.zorch:()Z
+  000a: move-result v0
+  000b: if-eqz v0, 0000 // -000b
+  000d: invoke-static {}, Blort.zorch:()Z
+  0010: goto 0007 // -0009
diff --git a/dx/tests/068-dex-infinite-loop/info.txt b/dx/tests/068-dex-infinite-loop/info.txt
new file mode 100644
index 0000000..358f0a8
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that a couple
+cases of (potentially) infinite loops translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/068-dex-infinite-loop/run b/dx/tests/068-dex-infinite-loop/run
new file mode 100644
index 0000000..3fe95cc
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/069-dex-source-position/Blort.java b/dx/tests/069-dex-source-position/Blort.java
new file mode 100644
index 0000000..5cede03
--- /dev/null
+++ b/dx/tests/069-dex-source-position/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test(int x) {
+        if (x == 0) { // line 6
+            return 1; // line 7
+        } else {
+            try {
+                x = test(x - 1); // line 10
+            } catch (RuntimeException ex) { // line 11
+                return 2; // line 12
+            }
+            x += test(x - 2); // line 14
+            return x; // line 15
+        }
+    }
+}
diff --git a/dx/tests/069-dex-source-position/expected.txt b/dx/tests/069-dex-source-position/expected.txt
new file mode 100644
index 0000000..b769055
--- /dev/null
+++ b/dx/tests/069-dex-source-position/expected.txt
@@ -0,0 +1,135 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+  debug info
+    line_start: 20
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0000: line 20
+    0004: line 21
+    0006: line 29
+    line = 24
+    0007: line 24
+    000f: line 28
+    0019: line 29
+    001c: line 25
+    001e: line 26
+    end sequence
+  source file: "Blort.java"
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+  debug info
+    line_start: 20
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0000: line 20
+    0004: line 21
+    0006: line 29
+    line = 24
+    0007: line 24
+    000f: line 28
+    0019: line 29
+    001c: line 25
+    001e: line 26
+    end sequence
+  source file: "Blort.java"
diff --git a/dx/tests/069-dex-source-position/info.txt b/dx/tests/069-dex-source-position/info.txt
new file mode 100644
index 0000000..28c8b51
--- /dev/null
+++ b/dx/tests/069-dex-source-position/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that source
+position information is faithfully reproduced (or not, as directed).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/069-dex-source-position/run b/dx/tests/069-dex-source-position/run
new file mode 100644
index 0000000..98c2630
--- /dev/null
+++ b/dx/tests/069-dex-source-position/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=important --no-locals \
+    --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=lines --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/070-dex-multianewarray/Blort.java b/dx/tests/070-dex-multianewarray/Blort.java
new file mode 100644
index 0000000..500b14c
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/Blort.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static Object test01() {
+        Object[][] x = new Object[2][5];
+        return x;
+    }
+
+    public static Object test02() {
+        Object[][][] x = new Object[4][1][];
+        return x;
+    }
+
+    public static Object test03() {
+        Object[][][] x = new Object[7][2][4];
+        return x;
+    }
+
+    public static Object test04() {
+        Object[][][] x = new Object[3][0][0];
+        return x;
+    }
+
+    public static Object test05() {
+        Object[][][][] x = new Object[1][3][5][7];
+        return x;
+    }
+
+    public static Object test06() {
+        Object[][][][][] x = new Object[8][7][2][3][4];
+        return x;
+    }
+
+    public static Object test07() {
+        Object[][][][][][] x = new Object[8][7][2][3][4][];
+        return x;
+    }
+
+    public static Object test08() {
+        Object[][][][][][][] x = new Object[8][7][2][3][4][][];
+        return x;
+    }
+
+    public static boolean[][] test09() {
+        return new boolean[1][2];
+    }
+
+    public static byte[][] test10() {
+        return new byte[3][4];
+    }
+
+    public static char[][] test11() {
+        return new char[5][6];
+    }
+
+    public static double[][] test12() {
+        return new double[7][8];
+    }
+
+    public static float[][] test13() {
+        return new float[9][1];
+    }
+
+    public static int[][][] test14() {
+        return new int[5][3][2];
+    }
+
+    public static long[][][] test15() {
+        return new long[3][4][7];
+    }
+
+    public static short[][][][] test16() {
+        return new short[5][4][3][2];
+    }
+
+    public static String[][][][][] test17() {
+        return new String[5][4][3][2][1];
+    }
+
+    public static Runnable[][][][][][] test18() {
+        return new Runnable[5][4][3][2][1][8];
+    }
+}
diff --git a/dx/tests/070-dex-multianewarray/expected.txt b/dx/tests/070-dex-multianewarray/expected.txt
new file mode 100644
index 0000000..e9fac8f
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/expected.txt
@@ -0,0 +1,247 @@
+javac 1.7.0-internal_bootstrap
+Blort.test01:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 2 // #2
+  0001: const/4 v2, #int 5 // #5
+  0002: filled-new-array {v1, v2}, int[]
+  0005: move-result-object v2
+  0006: const-class v1, java.lang.Object
+  0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v1
+  000c: check-cast v1, java.lang.Object[][]
+  000e: move-object v0, v1
+  000f: move-object v1, v0
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 4 // #4
+  0001: const/4 v2, #int 1 // #1
+  0002: filled-new-array {v1, v2}, int[]
+  0005: move-result-object v2
+  0006: const-class v1, java.lang.Object[]
+  0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v1
+  000c: check-cast v1, java.lang.Object[][][]
+  000e: move-object v0, v1
+  000f: move-object v1, v0
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test03:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 7 // #7
+  0001: const/4 v2, #int 2 // #2
+  0002: const/4 v3, #int 4 // #4
+  0003: filled-new-array {v1, v2, v3}, int[]
+  0006: move-result-object v2
+  0007: const-class v1, java.lang.Object
+  0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v1
+  000d: check-cast v1, java.lang.Object[][][]
+  000f: move-object v0, v1
+  0010: move-object v1, v0
+  0011: move-object v0, v1
+  0012: return-object v0
+Blort.test04:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 3 // #3
+  0001: const/4 v2, #int 0 // #0
+  0002: const/4 v3, #int 0 // #0
+  0003: filled-new-array {v1, v2, v3}, int[]
+  0006: move-result-object v2
+  0007: const-class v1, java.lang.Object
+  0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v1
+  000d: check-cast v1, java.lang.Object[][][]
+  000f: move-object v0, v1
+  0010: move-object v1, v0
+  0011: move-object v0, v1
+  0012: return-object v0
+Blort.test05:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v2, #int 3 // #3
+  0002: const/4 v3, #int 5 // #5
+  0003: const/4 v4, #int 7 // #7
+  0004: filled-new-array {v1, v2, v3, v4}, int[]
+  0007: move-result-object v2
+  0008: const-class v1, java.lang.Object
+  000a: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000d: move-result-object v1
+  000e: check-cast v1, java.lang.Object[][][][]
+  0010: move-object v0, v1
+  0011: move-object v1, v0
+  0012: move-object v0, v1
+  0013: return-object v0
+Blort.test06:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test07:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object[]
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test08:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object[][]
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test09:()[[Z:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 1 // #1
+  0001: const/4 v1, #int 2 // #2
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Boolean.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, boolean[][]
+  000e: return-object v0
+Blort.test10:()[[B:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 3 // #3
+  0001: const/4 v1, #int 4 // #4
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Byte.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, byte[][]
+  000e: return-object v0
+Blort.test11:()[[C:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 6 // #6
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Character.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, char[][]
+  000e: return-object v0
+Blort.test12:()[[D:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 7 // #7
+  0001: const/16 v1, #int 8 // #0008
+  0003: filled-new-array {v0, v1}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Double.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, double[][]
+  000f: return-object v0
+Blort.test13:()[[F:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/16 v0, #int 9 // #0009
+  0002: const/4 v1, #int 1 // #1
+  0003: filled-new-array {v0, v1}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Float.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, float[][]
+  000f: return-object v0
+Blort.test14:()[[[I:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 3 // #3
+  0002: const/4 v2, #int 2 // #2
+  0003: filled-new-array {v0, v1, v2}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Integer.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, int[][][]
+  000f: return-object v0
+Blort.test15:()[[[J:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 3 // #3
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 7 // #7
+  0003: filled-new-array {v0, v1, v2}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Long.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, long[][][]
+  000f: return-object v0
+Blort.test16:()[[[[S:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: filled-new-array {v0, v1, v2, v3}, int[]
+  0007: move-result-object v1
+  0008: sget-object v0, java.lang.Short.TYPE:Ljava/lang/Class;
+  000a: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000d: move-result-object v0
+  000e: check-cast v0, short[][][][]
+  0010: return-object v0
+Blort.test17:()[[[[[Ljava/lang/String;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 1 // #1
+  0005: filled-new-array {v0, v1, v2, v3, v4}, int[]
+  0008: move-result-object v1
+  0009: const-class v0, java.lang.String
+  000b: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000e: move-result-object v0
+  000f: check-cast v0, java.lang.String[][][][][]
+  0011: return-object v0
+Blort.test18:()[[[[[[Ljava/lang/Runnable;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 1 // #1
+  0005: const/16 v5, #int 8 // #0008
+  0007: filled-new-array/range {v0..v5}, int[]
+  000a: move-result-object v1
+  000b: const-class v0, java.lang.Runnable
+  000d: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  0010: move-result-object v0
+  0011: check-cast v0, java.lang.Runnable[][][][][][]
+  0013: return-object v0
diff --git a/dx/tests/070-dex-multianewarray/info.txt b/dx/tests/070-dex-multianewarray/info.txt
new file mode 100644
index 0000000..1251f0c
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that a few
+cases of multidimensional array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/070-dex-multianewarray/run b/dx/tests/070-dex-multianewarray/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/071-dex-java-stack-ops/blort.j b/dx/tests/071-dex-java-stack-ops/blort.j
new file mode 100644
index 0000000..848a84e
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/blort.j
@@ -0,0 +1,319 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort
+.super java/lang/Object
+
+; Methods to "consume" an int.
+.method public static consume1(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume2(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume3(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume4(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume5(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume6(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+; Methods to "consume" a long.
+.method public static consume1(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume2(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume3(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume4(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+; Test of "pop" opcode. This should end up causing a call to consume1(0).
+.method public static test_pop()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    iconst_1
+    pop          ; A1 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "pop2" opcode, form 1. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form1()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    pop2         ; A1 B1 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "pop2" opcode, form 2. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form2()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    lconst_0
+    pop2         ; A2 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "dup" opcode. This should end up causing these calls in order:
+; consume1(0), consume2(0).
+.method public static test_dup()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    dup          ; A1 -> A1 A1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    return
+.end method
+
+; Test of "dup_x1" opcode. This should end up causing these calls in order:
+; consume1(1), consume2(0), consume3(1).
+.method public static test_dup_x1()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    iconst_1
+    dup_x1       ; A1 B1 -> B1 A1 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    return
+.end method
+
+; Test of "dup_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0), consume4(2).
+.method public static test_dup_x2_form1()V
+.limit stack 4
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    dup_x2       ; A1 B1 C1 -> C1 A1 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    return
+.end method
+
+; Test of "dup_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(1), consume2(0L), consume3(1).
+.method public static test_dup_x2_form2()V
+.limit stack 4
+.limit locals 0
+    lconst_0
+    iconst_1
+    dup_x2       ; A2 B1 -> B1 A2 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(J)V
+    invokestatic Blort/consume3(I)V
+    return
+.end method
+
+; Test of "dup2" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(0), consume3(1), consume4(0).
+.method public static test_dup2_form1()V
+.limit stack 4
+.limit locals 0
+    iconst_0
+    iconst_1
+    dup2         ; A1 B1 -> A1 B1 A1 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    return
+.end method
+
+; Test of "dup2" opcode, form 2. This should end up causing these calls
+; in order: consume1(0L), consume2(0L).
+.method public static test_dup2_form2()V
+.limit stack 4
+.limit locals 0
+    lconst_0
+    dup2         ; A2 -> A2 A2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(J)V
+    return
+.end method
+
+; Test of "dup2_x1" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(2), consume3(0), consume4(1), consume5(2).
+.method public static test_dup2_x1_form1()V
+.limit stack 5
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    dup2_x1      ; A1 B1 C1 -> B1 C1 A1 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    return
+.end method
+
+
+; Test of "dup2_x1" opcode, form 2. This should end up causing these calls
+; in order: consume1(1L), consume2(2), consume3(1L).
+.method public static test_dup2_x1_form2()V
+.limit stack 5
+.limit locals 0
+    iconst_0
+    lconst_1
+    dup2_x1      ; A1 B2 -> B2 A1 B2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(J)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(3), consume2(2), consume3(1), consume4(0), consume5(3),
+; consume6(2).
+.method public static test_dup2_x2_form1()V
+.limit stack 6
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    iconst_3
+    dup2_x2      ; A1 B1 C1 D1 -> C1 D1 A1 B1 C1 D1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    invokestatic Blort/consume6(I)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(2L), consume2(1), consume3(0), consume4(2L).
+.method public static test_dup2_x2_form2()V
+.limit stack 6
+.limit locals 0
+    iconst_0
+    iconst_1
+    ldc2_w 2
+    dup2_x2      ; A1 B1 C2 -> C2 A1 B1 C2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(J)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 3. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0L), consume4(2), consume5(1).
+.method public static test_dup2_x2_form3()V
+.limit stack 6
+.limit locals 0
+    lconst_0
+    iconst_1
+    iconst_2
+    dup2_x2      ; A2 B1 C1 -> B1 C1 A2 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(J)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 4. This should end up causing these calls
+; in order: consume1(1L), consume2(0L), consume3(1L).
+.method public static test_dup2_x2_form4()V
+.limit stack 6
+.limit locals 0
+    lconst_0
+    lconst_1
+    dup2_x2      ; A2 B2 -> B2 A2 B2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(J)V
+    invokestatic Blort/consume3(J)V
+    return
+.end method
+
+; Test of "swap" opcode. This should end up causing these calls
+; in order: consume1(0), consume2(1).
+.method public static test_swap()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    iconst_1
+    swap         ; A1 B1 -> B1 A1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    return
+.end method
diff --git a/dx/tests/071-dex-java-stack-ops/expected.txt b/dx/tests/071-dex-java-stack-ops/expected.txt
new file mode 100644
index 0000000..3ba8ef3
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/expected.txt
@@ -0,0 +1,210 @@
+Blort.test_dup:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: move v2, v0
+  0002: move v0, v2
+  0003: move v1, v2
+  0004: invoke-static {v1}, Blort.consume1:(I)V
+  0007: invoke-static {v0}, Blort.consume2:(I)V
+  000a: return-void
+Blort.test_dup2_form1:()V:
+regs: 0006; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v4, v0
+  0003: move v5, v1
+  0004: move v0, v4
+  0005: move v1, v5
+  0006: move v2, v4
+  0007: move v3, v5
+  0008: invoke-static {v3}, Blort.consume1:(I)V
+  000b: invoke-static {v2}, Blort.consume2:(I)V
+  000e: invoke-static {v1}, Blort.consume3:(I)V
+  0011: invoke-static {v0}, Blort.consume4:(I)V
+  0014: return-void
+Blort.test_dup2_form2:()V:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: move-wide v4, v0
+  0003: move-wide v0, v4
+  0004: move-wide v2, v4
+  0005: invoke-static {v2, v3}, Blort.consume1:(J)V
+  0008: invoke-static {v0, v1}, Blort.consume2:(J)V
+  000b: return-void
+Blort.test_dup2_x1_form1:()V:
+regs: 0008; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: move v5, v0
+  0004: move v6, v1
+  0005: move v7, v2
+  0006: move v0, v6
+  0007: move v1, v7
+  0008: move v2, v5
+  0009: move v3, v6
+  000a: move v4, v7
+  000b: invoke-static {v4}, Blort.consume1:(I)V
+  000e: invoke-static {v3}, Blort.consume2:(I)V
+  0011: invoke-static {v2}, Blort.consume3:(I)V
+  0014: invoke-static {v1}, Blort.consume4:(I)V
+  0017: invoke-static {v0}, Blort.consume5:(I)V
+  001a: return-void
+Blort.test_dup2_x1_form2:()V:
+regs: 0008; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 0 // #0
+  0001: const-wide/16 v1, #long 1 // #0001
+  0003: move v5, v0
+  0004: move-wide v6, v1
+  0005: move-wide v0, v6
+  0006: move v2, v5
+  0007: move-wide v3, v6
+  0008: invoke-static {v3, v4}, Blort.consume1:(J)V
+  000b: invoke-static {v2}, Blort.consume2:(I)V
+  000e: invoke-static {v0, v1}, Blort.consume3:(J)V
+  0011: return-void
+Blort.test_dup2_x2_form1:()V:
+regs: 000a; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: const/4 v3, #int 3 // #3
+  0004: move v6, v0
+  0005: move v7, v1
+  0006: move v8, v2
+  0007: move v9, v3
+  0008: move v0, v8
+  0009: move v1, v9
+  000a: move v2, v6
+  000b: move v3, v7
+  000c: move v4, v8
+  000d: move v5, v9
+  000e: invoke-static {v5}, Blort.consume1:(I)V
+  0011: invoke-static {v4}, Blort.consume2:(I)V
+  0014: invoke-static {v3}, Blort.consume3:(I)V
+  0017: invoke-static {v2}, Blort.consume4:(I)V
+  001a: invoke-static {v1}, Blort.consume5:(I)V
+  001d: invoke-static {v0}, Blort.consume6:(I)V
+  0020: return-void
+Blort.test_dup2_x2_form2:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const-wide/16 v2, #long 2 // #0002
+  0004: move v6, v0
+  0005: move v7, v1
+  0006: move-wide v8, v2
+  0007: move-wide v0, v8
+  0008: move v2, v6
+  0009: move v3, v7
+  000a: move-wide v4, v8
+  000b: invoke-static {v4, v5}, Blort.consume1:(J)V
+  000e: invoke-static {v3}, Blort.consume2:(I)V
+  0011: invoke-static {v2}, Blort.consume3:(I)V
+  0014: invoke-static {v0, v1}, Blort.consume4:(J)V
+  0017: return-void
+Blort.test_dup2_x2_form3:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const/4 v2, #int 1 // #1
+  0003: const/4 v3, #int 2 // #2
+  0004: move-wide v6, v0
+  0005: move v8, v2
+  0006: move v9, v3
+  0007: move v0, v8
+  0008: move v1, v9
+  0009: move-wide v2, v6
+  000a: move v4, v8
+  000b: move v5, v9
+  000c: invoke-static {v5}, Blort.consume1:(I)V
+  000f: invoke-static {v4}, Blort.consume2:(I)V
+  0012: invoke-static {v2, v3}, Blort.consume3:(J)V
+  0015: invoke-static {v1}, Blort.consume4:(I)V
+  0018: invoke-static {v0}, Blort.consume5:(I)V
+  001b: return-void
+Blort.test_dup2_x2_form4:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const-wide/16 v2, #long 1 // #0001
+  0004: move-wide v6, v0
+  0005: move-wide v8, v2
+  0006: move-wide v0, v8
+  0007: move-wide v2, v6
+  0008: move-wide v4, v8
+  0009: invoke-static {v4, v5}, Blort.consume1:(J)V
+  000c: invoke-static {v2, v3}, Blort.consume2:(J)V
+  000f: invoke-static {v0, v1}, Blort.consume3:(J)V
+  0012: return-void
+Blort.test_dup_x1:()V:
+regs: 0005; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v3, v0
+  0003: move v4, v1
+  0004: move v0, v4
+  0005: move v1, v3
+  0006: move v2, v4
+  0007: invoke-static {v2}, Blort.consume1:(I)V
+  000a: invoke-static {v1}, Blort.consume2:(I)V
+  000d: invoke-static {v0}, Blort.consume3:(I)V
+  0010: return-void
+Blort.test_dup_x2_form1:()V:
+regs: 0007; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: move v4, v0
+  0004: move v5, v1
+  0005: move v6, v2
+  0006: move v0, v6
+  0007: move v1, v4
+  0008: move v2, v5
+  0009: move v3, v6
+  000a: invoke-static {v3}, Blort.consume1:(I)V
+  000d: invoke-static {v2}, Blort.consume2:(I)V
+  0010: invoke-static {v1}, Blort.consume3:(I)V
+  0013: invoke-static {v0}, Blort.consume4:(I)V
+  0016: return-void
+Blort.test_dup_x2_form2:()V:
+regs: 0007; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const/4 v2, #int 1 // #1
+  0003: move-wide v4, v0
+  0004: move v6, v2
+  0005: move v0, v6
+  0006: move-wide v1, v4
+  0007: move v3, v6
+  0008: invoke-static {v3}, Blort.consume1:(I)V
+  000b: invoke-static {v1, v2}, Blort.consume2:(J)V
+  000e: invoke-static {v0}, Blort.consume3:(I)V
+  0011: return-void
+Blort.test_pop:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: invoke-static {v0}, Blort.consume1:(I)V
+  0005: return-void
+Blort.test_pop2_form1:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: invoke-static {v0}, Blort.consume1:(I)V
+  0006: return-void
+Blort.test_pop2_form2:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const-wide/16 v1, #long 0 // #0000
+  0003: invoke-static {v0}, Blort.consume1:(I)V
+  0006: return-void
+Blort.test_swap:()V:
+regs: 0004; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v2, v0
+  0003: move v3, v1
+  0004: move v0, v3
+  0005: move v1, v2
+  0006: invoke-static {v1}, Blort.consume1:(I)V
+  0009: invoke-static {v0}, Blort.consume2:(I)V
+  000c: return-void
diff --git a/dx/tests/071-dex-java-stack-ops/info.txt b/dx/tests/071-dex-java-stack-ops/info.txt
new file mode 100644
index 0000000..6c5383a
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that at
+least one case of each of the possible forms of Java stack
+manipulation op translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/071-dex-java-stack-ops/run b/dx/tests/071-dex-java-stack-ops/run
new file mode 100644
index 0000000..52d8a77
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/072-dex-switch-edge-cases/Blort.java b/dx/tests/072-dex-switch-edge-cases/Blort.java
new file mode 100644
index 0000000..ba2e033
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/Blort.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    // Empty switch statement. (Note: This is the same as a default-only
+    // switch statement, since under the covers every switch statement
+    // has a default of some sort.)
+    public int test1(int x) {
+        switch (x) {
+            // This space intentionally left blank.
+        }
+
+        return 0;
+    }
+
+    // Single element.
+    public int test2(int x) {
+        switch (x) {
+            case 0: return 0;
+        }
+
+        return 1;
+    }
+
+    // Single element: Integer.MIN_VALUE.
+    public int test3(int x) {
+        switch (x) {
+            case Integer.MIN_VALUE: return 0;
+        }
+
+        return 1;
+    }
+    
+    // Single element: Integer.MAX_VALUE.
+    public int test4(int x) {
+        switch (x) {
+            case Integer.MAX_VALUE: return 0;
+        }
+
+        return 1;
+    }
+
+    // Two elements: 0 and Integer.MIN_VALUE.
+    public int test5(int x) {
+        switch (x) {
+            case 0: return 0;
+            case Integer.MIN_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: 0 and Integer.MAX_VALUE.
+    public int test6(int x) {
+        switch (x) {
+            case 0: return 0;
+            case Integer.MAX_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: Integer.MIN_VALUE and Integer.MAX_VALUE.
+    public int test7(int x) {
+        switch (x) {
+            case Integer.MIN_VALUE: return 0;
+            case Integer.MAX_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: Large enough to be packed but such that 32 bit
+    // threshold calculations could overflow.
+    public int test8(int x) {
+        switch (x) {
+            case 0: return 0;
+            case 0x4cccccc8: return 1;
+        }
+
+        return 2;
+    }
+}
diff --git a/dx/tests/072-dex-switch-edge-cases/expected.txt b/dx/tests/072-dex-switch-edge-cases/expected.txt
new file mode 100644
index 0000000..8be71da
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/expected.txt
@@ -0,0 +1,127 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 0 // #0
+  0004: move v0, v2
+  0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+Blort.test3:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          -2147483648: 00000009 // +00000006
+Blort.test4:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          2147483647: 00000009 // +00000006
+Blort.test5:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          -2147483648: 0000000c // +00000009
+          0: 00000009 // +00000006
+Blort.test6:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+          2147483647: 0000000c // +00000009
+Blort.test7:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          -2147483648: 00000009 // +00000006
+          2147483647: 0000000c // +00000009
+Blort.test8:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+          1288490184: 0000000c // +00000009
diff --git a/dx/tests/072-dex-switch-edge-cases/info.txt b/dx/tests/072-dex-switch-edge-cases/info.txt
new file mode 100644
index 0000000..4c7b42c
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of switch op edge cases get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/072-dex-switch-edge-cases/run b/dx/tests/072-dex-switch-edge-cases/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/073-dex-null-array-refs/Blort.java b/dx/tests/073-dex-null-array-refs/Blort.java
new file mode 100644
index 0000000..b6678c0
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static Object test1() {
+        return ((Object[]) null)[0];
+    }
+
+    public static void test2() {
+        ((Object[]) null)[0] = null;
+    }
+
+    public static int test3() {
+        return ((Object[]) null).length;
+    }
+
+    public static Object test4() {
+        Object[] arr = null;
+        return arr[0];
+    }
+
+    public static void test5() {
+        Object[] arr = null;
+        arr[0] = null;
+    }
+
+    public static int test6() {
+        Object[] arr = null;
+        return arr.length;
+    }
+
+    public static Object test7(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        return arr[0];
+    }
+    
+    public static void test8(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        arr[0] = null;
+    }
+
+    public static int test9(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        return arr.length;
+    }
+
+    public static boolean check() {
+        return true;
+    }
+}
diff --git a/dx/tests/073-dex-null-array-refs/expected.txt b/dx/tests/073-dex-null-array-refs/expected.txt
new file mode 100644
index 0000000..3acb6b2
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/expected.txt
@@ -0,0 +1,86 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: const/4 v1, #int 0 // #0
+  0004: aget-object v0, v0, v1
+  0006: return-object v0
+Blort.test2:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: const/4 v1, #int 0 // #0
+  0004: const/4 v2, #null // #0
+  0005: aput-object v2, v0, v1
+  0007: return-void
+Blort.test3:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: array-length v0, v0
+  0004: return v0
+Blort.test4:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: aget-object v1, v1, v2
+  0006: move-object v0, v1
+  0007: return-object v0
+Blort.test5:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: const/4 v3, #null // #0
+  0005: aput-object v3, v1, v2
+  0007: return-void
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: array-length v1, v1
+  0004: move v0, v1
+  0005: return v0
+Blort.test7:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 0 // #0
+  000b: aget-object v1, v1, v2
+  000d: move-object v0, v1
+  000e: return-object v0
+Blort.test8:([Ljava/lang/Object;)V:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move-object v0, v4
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 0 // #0
+  000b: const/4 v3, #null // #0
+  000c: aput-object v3, v1, v2
+  000e: return-void
+Blort.test9:([Ljava/lang/Object;)I:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: array-length v1, v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/073-dex-null-array-refs/info.txt b/dx/tests/073-dex-null-array-refs/info.txt
new file mode 100644
index 0000000..ca3b161
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of cases convert reasonably, where necessarily or possibly
+null array references are used.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/073-dex-null-array-refs/run b/dx/tests/073-dex-null-array-refs/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/074-dex-form35c-edge-case/Blort.java b/dx/tests/074-dex-form35c-edge-case/Blort.java
new file mode 100644
index 0000000..60efc63
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/Blort.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public void test()
+    {
+        int i1 = 0;
+        int i2 = 0;
+        int i3 = 0;
+        int i4 = 0;
+        int i5 = 0;
+        int i6 = 0;
+        int i7 = 0;
+        int i8 = 0;
+        int i9 = 0;
+        int i10 = 0;
+        int i11 = 0;
+        int i12 = 0;
+        int i13 = 0;
+
+        blort(0);
+    }
+
+    public void blort(long x) {
+        // blank
+    }
+}
diff --git a/dx/tests/074-dex-form35c-edge-case/expected.txt b/dx/tests/074-dex-form35c-edge-case/expected.txt
new file mode 100644
index 0000000..bc4f2da
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/expected.txt
@@ -0,0 +1,34 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()V:
+regs: 0012; ins: 0001; outs: 0003
+  0000: move-object/from16 v0, v17
+  0002: const/4 v14, #int 0 // #0
+  0003: move v1, v14
+  0004: const/4 v14, #int 0 // #0
+  0005: move v2, v14
+  0006: const/4 v14, #int 0 // #0
+  0007: move v3, v14
+  0008: const/4 v14, #int 0 // #0
+  0009: move v4, v14
+  000a: const/4 v14, #int 0 // #0
+  000b: move v5, v14
+  000c: const/4 v14, #int 0 // #0
+  000d: move v6, v14
+  000e: const/4 v14, #int 0 // #0
+  000f: move v7, v14
+  0010: const/4 v14, #int 0 // #0
+  0011: move v8, v14
+  0012: const/4 v14, #int 0 // #0
+  0013: move v9, v14
+  0014: const/4 v14, #int 0 // #0
+  0015: move v10, v14
+  0016: const/4 v14, #int 0 // #0
+  0017: move v11, v14
+  0018: const/4 v14, #int 0 // #0
+  0019: move v12, v14
+  001a: const/4 v14, #int 0 // #0
+  001b: move v13, v14
+  001c: move-object v14, v0
+  001d: const-wide/16 v15, #long 0 // #0000
+  001f: invoke-virtual/range {v14..v16}, Blort.blort:(J)V
+  0022: return-void
diff --git a/dx/tests/074-dex-form35c-edge-case/info.txt b/dx/tests/074-dex-form35c-edge-case/info.txt
new file mode 100644
index 0000000..51d83bd
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to see that
+an edge case of instruction format 35c works, where a reference
+is made to register 15 as a category-2 value, meaning that
+the instruction has to be rewritten to use a different format.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/074-dex-form35c-edge-case/run b/dx/tests/074-dex-form35c-edge-case/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/075-dex-cat2-value-merge/Blort.java b/dx/tests/075-dex-cat2-value-merge/Blort.java
new file mode 100644
index 0000000..d1bf0be
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void test(long[] arr)
+    {
+        long x = 0;
+        
+        for (;;) {
+            x += arr[0];
+        }
+    }
+}
diff --git a/dx/tests/075-dex-cat2-value-merge/expected.txt b/dx/tests/075-dex-cat2-value-merge/expected.txt
new file mode 100644
index 0000000..9bb8e33
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/expected.txt
@@ -0,0 +1,13 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:([J)V:
+regs: 0008; ins: 0001; outs: 0000
+  0000: move-object v0, v7
+  0001: const-wide/16 v3, #long 0 // #0000
+  0003: move-wide v1, v3
+  0004: move-wide v3, v1
+  0005: move-object v5, v0
+  0006: const/4 v6, #int 0 // #0
+  0007: aget-wide v5, v5, v6
+  0009: add-long/2addr v3, v5
+  000a: move-wide v1, v3
+  000b: goto 0004 // -0007
diff --git a/dx/tests/075-dex-cat2-value-merge/info.txt b/dx/tests/075-dex-cat2-value-merge/info.txt
new file mode 100644
index 0000000..de411b8
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+when a known value of category-2 gets merged during control
+flow analysis, things don't break.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/075-dex-cat2-value-merge/run b/dx/tests/075-dex-cat2-value-merge/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/076-dex-synch-and-stack/Blort.java b/dx/tests/076-dex-synch-and-stack/Blort.java
new file mode 100644
index 0000000..6ba9757
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public synchronized void test() {
+        new Object();
+    }
+}
diff --git a/dx/tests/076-dex-synch-and-stack/expected.txt b/dx/tests/076-dex-synch-and-stack/expected.txt
new file mode 100644
index 0000000..c8c0178
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/expected.txt
@@ -0,0 +1,20 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()V:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move-object v0, v5
+  0001: move-object v3, v5
+  0002: monitor-enter v3
+  0003: new-instance v1, java.lang.Object
+  0005: move-object v4, v1
+  0006: move-object v1, v4
+  0007: move-object v2, v4
+  0008: invoke-direct {v2}, java.lang.Object.<init>:()V
+  000b: monitor-exit v3
+  000c: return-void
+  000d: move-exception v0
+  000e: monitor-exit v3
+  000f: throw v0
+  catches
+    tries:
+      try 0003..000b
+      catch <any> -> 000d
diff --git a/dx/tests/076-dex-synch-and-stack/info.txt b/dx/tests/076-dex-synch-and-stack/info.txt
new file mode 100644
index 0000000..ab5206f
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the synchronized method conversion doesn't interact poorly with stack
+operation unwinding.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/076-dex-synch-and-stack/run b/dx/tests/076-dex-synch-and-stack/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/077-dex-code-alignment/Blort.java b/dx/tests/077-dex-code-alignment/Blort.java
new file mode 100644
index 0000000..862cd51
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void justReturn1() {
+        // This space intentionally left blank.
+    }
+
+    public static void justReturn2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/077-dex-code-alignment/expected.txt b/dx/tests/077-dex-code-alignment/expected.txt
new file mode 100644
index 0000000..8f6db7d
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/expected.txt
@@ -0,0 +1 @@
+javac 1.7.0-internal_bootstrap
diff --git a/dx/tests/077-dex-code-alignment/info.txt b/dx/tests/077-dex-code-alignment/info.txt
new file mode 100644
index 0000000..0dd662b
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+code arrays are 4-byte aligned within a dex file.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/077-dex-code-alignment/run b/dx/tests/077-dex-code-alignment/run
new file mode 100644
index 0000000..f311dbf
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/run
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+
+# The awk script below attempts to filter out everything but the
+# essentials: That methods justReturn1() and justReturn2() contain
+# a single "return-void" code unit, and that there is an empty (0x0000)
+# code unit between the two of them.
+
+dx --debug --dex --positions=none --no-locals --dump-to=- *.class | awk '
+BEGIN { codes = 0; dump = 0; }
+/codes:/ { codes = 1; }
+codes && /justReturn/ { dump = 1; print "method start"; }
+/string_data:/ { codes = 0; dump = 0; }
+dump && /^......: ....     / { print $2; }
+'
diff --git a/dx/tests/078-dex-local-variable-table/Blort.java b/dx/tests/078-dex-local-variable-table/Blort.java
new file mode 100644
index 0000000..d9f006f
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/Blort.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static void test01(Object x) {
+        x.hashCode();
+    }
+
+    public static Object test02() {
+        Object[] arr = null;
+        return arr[0];
+    }
+
+    public static String test03(int x) {
+        String foo = null;
+        return foo;
+    }
+
+    public static String test04(int x) {
+        String foo = null;
+        if (x < 0) {
+            foo = "bar";
+        }
+        return foo;
+    }
+
+    public static int test05(Object x) {
+        int[] arr = (int[]) x;
+        arr[0] = 123;
+        return arr[0];
+    }
+
+    public static int test06(int x) {
+        if (x < 10) {
+            int y = 1;
+            return y;
+        } else {
+            int y = 2;
+            return y;
+        }
+    }
+
+    // Test for representation of boolean.
+    public static void test07(boolean x) {
+        boolean y = x;
+    }
+
+    // Test for representation of byte.
+    public static void test08(byte x) {
+        byte y = x;
+    }
+
+    // Test for representation of char.
+    public static void test09(char x) {
+        char y = x;
+    }
+
+    // Test for representation of double.
+    public static void test10(double x) {
+        double y = x;
+    }
+
+    // Test for representation of float.
+    public static void test11(float x) {
+        float y = x;
+    }
+
+    // Test for representation of int.
+    public static void test12(int x) {
+        int y = x;
+    }
+
+    // Test for representation of long.
+    public static void test13(long x) {
+        long y = x;
+    }
+
+    // Test for representation of short.
+    public static void test14(short x) {
+        short y = x;
+    }
+
+    // Test for representation of Object.
+    public static void test15(Object x) {
+        Object y = x;
+    }
+
+    // Test for representation of String (as a token example of a non-Object
+    // reference type).
+    public static void test16(String x) {
+        String y = x;
+    }
+
+    // Test for representation of int[] (as a token example of an array class).
+    public static void test17(int[] x) {
+        int[] y = x;
+    }
+}
diff --git a/dx/tests/078-dex-local-variable-table/expected.txt b/dx/tests/078-dex-local-variable-table/expected.txt
new file mode 100644
index 0000000..d55333b
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/expected.txt
@@ -0,0 +1,315 @@
+javac 1.7.0-internal_bootstrap
+Blort.test01:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+  0005: move-result v1
+  0006: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v2
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    end sequence
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: aget-object v1, v1, v2
+  0006: move-object v0, v1
+  0007: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0000
+    0000: prologue end
+    0002: advance pc
+    0002: +local v0 arr java.lang.Object[]
+    0007: advance pc
+    0007: -local v0 arr java.lang.Object[]
+    end sequence
+Blort.test03:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move-object v2, v1
+  0004: move-object v0, v2
+  0005: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 foo java.lang.String
+    0005: advance pc
+    0005: -local v0 x int
+    end sequence
+Blort.test04:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move v2, v0
+  0004: if-gez v2, 0009 // +0005
+  0006: const-string v2, "bar"
+  0008: move-object v1, v2
+  0009: move-object v2, v1
+  000a: move-object v0, v2
+  000b: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 foo java.lang.String
+    000b: advance pc
+    000b: -local v0 x int
+    end sequence
+Blort.test05:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: check-cast v2, int[]
+  0004: check-cast v2, int[]
+  0006: move-object v1, v2
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 0 // #0
+  0009: const/16 v4, #int 123 // #007b
+  000b: aput v4, v2, v3
+  000d: move-object v2, v1
+  000e: const/4 v3, #int 0 // #0
+  000f: aget v2, v2, v3
+  0011: move v0, v2
+  0012: return v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    0007: advance pc
+    0007: +local v1 arr int[]
+    0012: advance pc
+    0012: -local v0 x java.lang.Object
+    end sequence
+Blort.test06:(I)I:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move v0, v4
+  0001: move v2, v0
+  0002: const/16 v3, #int 10 // #000a
+  0004: if-ge v2, v3, 000b // +0007
+  0006: const/4 v2, #int 1 // #1
+  0007: move v1, v2
+  0008: move v2, v1
+  0009: move v0, v2
+  000a: return v0
+  000b: const/4 v2, #int 2 // #2
+  000c: move v1, v2
+  000d: move v2, v1
+  000e: move v0, v2
+  000f: goto 000a // -0005
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v4
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0008: advance pc
+    0008: +local v1 y int
+    000a: advance pc
+    000a: -local v0 x int
+    000b: advance pc
+    000b: -local v1 y int
+    000b: +local restart v0 x int
+    000d: advance pc
+    000d: +local restart v1 y int
+    end sequence
+Blort.test07:(Z)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x boolean
+    0003: advance pc
+    0003: +local v1 y boolean
+    end sequence
+Blort.test08:(B)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x byte
+    0003: advance pc
+    0003: +local v1 y byte
+    end sequence
+Blort.test09:(C)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x char
+    0003: advance pc
+    0003: +local v1 y char
+    end sequence
+Blort.test10:(D)V:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-wide v0, v6
+  0001: move-wide v4, v0
+  0002: move-wide v2, v4
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v6
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x double
+    0003: advance pc
+    0003: +local v2 y double
+    end sequence
+Blort.test11:(F)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x float
+    0003: advance pc
+    0003: +local v1 y float
+    end sequence
+Blort.test12:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 y int
+    end sequence
+Blort.test13:(J)V:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-wide v0, v6
+  0001: move-wide v4, v0
+  0002: move-wide v2, v4
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v6
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x long
+    0003: advance pc
+    0003: +local v2 y long
+    end sequence
+Blort.test14:(S)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x short
+    0003: advance pc
+    0003: +local v1 y short
+    end sequence
+Blort.test15:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    0003: advance pc
+    0003: +local v1 y java.lang.Object
+    end sequence
+Blort.test16:(Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.String
+    0003: advance pc
+    0003: +local v1 y java.lang.String
+    end sequence
+Blort.test17:([I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int[]
+    0003: advance pc
+    0003: +local v1 y int[]
+    end sequence
diff --git a/dx/tests/078-dex-local-variable-table/info.txt b/dx/tests/078-dex-local-variable-table/info.txt
new file mode 100644
index 0000000..7834271
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables get emitted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/078-dex-local-variable-table/run b/dx/tests/078-dex-local-variable-table/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+    *.class
diff --git a/dx/tests/079-dex-local-variable-renumbering/Blort.java b/dx/tests/079-dex-local-variable-renumbering/Blort.java
new file mode 100644
index 0000000..629da90
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/Blort.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    public static int test1(int x) {
+        float f0 = 0.0f;
+        float f1 = 0.0f;
+        float f2 = 0.0f;
+        float f3 = 0.0f;
+        float f4 = 0.0f;
+        float f5 = 0.0f;
+        float f6 = 0.0f;
+        float f7 = 0.0f;
+        float f8 = 0.0f;
+        float f9 = 0.0f;
+        float f10 = 0.0f;
+        float f11 = 0.0f;
+        float f12 = 0.0f;
+        float f13 = 0.0f;
+        float f14 = 0.0f;
+        float f15 = 0.0f;
+        int x16 = x;
+        return -x16;
+    }
+}
diff --git a/dx/tests/079-dex-local-variable-renumbering/expected.txt b/dx/tests/079-dex-local-variable-renumbering/expected.txt
new file mode 100644
index 0000000..f8f15c8
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/expected.txt
@@ -0,0 +1,88 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(I)I:
+regs: 0015; ins: 0001; outs: 0000
+  0000: move/from16 v1, v20
+  0002: const/16 v19, #float 0.0 // #0000
+  0004: move/from16 v2, v19
+  0006: const/16 v19, #float 0.0 // #0000
+  0008: move/from16 v3, v19
+  000a: const/16 v19, #float 0.0 // #0000
+  000c: move/from16 v4, v19
+  000e: const/16 v19, #float 0.0 // #0000
+  0010: move/from16 v5, v19
+  0012: const/16 v19, #float 0.0 // #0000
+  0014: move/from16 v6, v19
+  0016: const/16 v19, #float 0.0 // #0000
+  0018: move/from16 v7, v19
+  001a: const/16 v19, #float 0.0 // #0000
+  001c: move/from16 v8, v19
+  001e: const/16 v19, #float 0.0 // #0000
+  0020: move/from16 v9, v19
+  0022: const/16 v19, #float 0.0 // #0000
+  0024: move/from16 v10, v19
+  0026: const/16 v19, #float 0.0 // #0000
+  0028: move/from16 v11, v19
+  002a: const/16 v19, #float 0.0 // #0000
+  002c: move/from16 v12, v19
+  002e: const/16 v19, #float 0.0 // #0000
+  0030: move/from16 v13, v19
+  0032: const/16 v19, #float 0.0 // #0000
+  0034: move/from16 v14, v19
+  0036: const/16 v19, #float 0.0 // #0000
+  0038: move/from16 v15, v19
+  003a: const/16 v19, #float 0.0 // #0000
+  003c: move/from16 v16, v19
+  003e: const/16 v19, #float 0.0 // #0000
+  0040: move/from16 v17, v19
+  0042: move/from16 v19, v1
+  0044: move/from16 v18, v19
+  0046: move/from16 v19, v18
+  0048: move/from16 v0, v19
+  004a: neg-int v0, v0
+  004b: move/from16 v19, v0
+  004d: move/from16 v1, v19
+  004f: return v1
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v20
+    0000: prologue end
+    0002: advance pc
+    0002: +local v1 x int
+    0006: advance pc
+    0006: +local v2 f0 float
+    000a: advance pc
+    000a: +local v3 f1 float
+    000e: advance pc
+    000e: +local v4 f2 float
+    0012: advance pc
+    0012: +local v5 f3 float
+    0016: advance pc
+    0016: +local v6 f4 float
+    001a: advance pc
+    001a: +local v7 f5 float
+    001e: advance pc
+    001e: +local v8 f6 float
+    0022: advance pc
+    0022: +local v9 f7 float
+    0026: advance pc
+    0026: +local v10 f8 float
+    002a: advance pc
+    002a: +local v11 f9 float
+    002e: advance pc
+    002e: +local v12 f10 float
+    0032: advance pc
+    0032: +local v13 f11 float
+    0036: advance pc
+    0036: +local v14 f12 float
+    003a: advance pc
+    003a: +local v15 f13 float
+    003e: advance pc
+    003e: +local v16 f14 float
+    0042: advance pc
+    0042: +local v17 f15 float
+    0046: advance pc
+    0046: +local v18 x16 int
+    004f: advance pc
+    004f: -local v1 x int
+    end sequence
diff --git a/dx/tests/079-dex-local-variable-renumbering/info.txt b/dx/tests/079-dex-local-variable-renumbering/info.txt
new file mode 100644
index 0000000..249b23f
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables stay in sync when the register set gets renumbered
+to make room for low scratch registers.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/079-dex-local-variable-renumbering/run b/dx/tests/079-dex-local-variable-renumbering/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+    *.class
diff --git a/dx/tests/080-dex-exception-tables/Blort.java b/dx/tests/080-dex-exception-tables/Blort.java
new file mode 100644
index 0000000..f406bee
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/Blort.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+    public static void call1() { }
+    public static void call2() { }
+    public static void call3() { }
+    public static void call4() { }
+    public static void call5() { }
+
+    public static int test1() {
+        try {
+            call1();
+            call2();
+        } catch (IndexOutOfBoundsException ex) {
+            return 10;
+        } catch (RuntimeException ex) {
+            return 11;
+        } 
+
+        call3();
+        return 12;
+    }
+
+    public static int test2() {
+        try {
+            call1();
+            try {
+                call2();
+            } catch (IndexOutOfBoundsException ex) {
+                return 10;
+            }
+            call3();
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        return 12;
+    }
+
+    public static int test3() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+                call4();
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call5();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test4() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call5();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test5() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test6() {
+        try {
+            try {
+                try {
+                    call1();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+                call2();
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call3();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        call4();
+        return 13;
+    }
+
+    public static int test7() {
+        try {
+            call1();
+        } catch (RuntimeException ex) {
+            return 10;
+        } 
+
+        try {
+            call2();
+        } catch (RuntimeException ex) {
+            return 11;
+        } 
+
+        return 12;
+    }
+
+    public static int test8() {
+        try {
+            call1();
+            call2();
+        } catch (RuntimeException ex) {
+            return 10;
+        } 
+
+        try {
+            call3();
+            call4();
+        } catch (RuntimeException ex) {
+            return 11;
+        } 
+
+        return 12;
+    }
+
+    public static int test9() {
+        try {
+            call1();
+            try {
+                call2();
+            } catch (IllegalArgumentException ex) {
+                return 10;
+            }
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        try {
+            call3();
+            try {
+                call4();
+            } catch (IllegalArgumentException ex) {
+                return 12;
+            }
+        } catch (RuntimeException ex) {
+            return 13;
+        }
+
+        return 14;
+    }
+    
+}
diff --git a/dx/tests/080-dex-exception-tables/expected.txt b/dx/tests/080-dex-exception-tables/expected.txt
new file mode 100644
index 0000000..9337e73
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/expected.txt
@@ -0,0 +1,287 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 12 // #000c
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  catches
+    tries:
+      try 0000..0006
+      catch java.lang.IndexOutOfBoundsException -> 000d,
+        java.lang.RuntimeException -> 0013
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 12 // #000c
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0013
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 000d,
+        java.lang.RuntimeException -> 0013
+      try 0006..0009
+      catch java.lang.RuntimeException -> 0013
+Blort.test3:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: invoke-static {}, Blort.call5:()V
+  000f: const/16 v1, #int 13 // #000d
+  0011: move v0, v1
+  0012: return v0
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 10 // #000a
+  0017: move v0, v1
+  0018: goto 0012 // -0006
+  0019: move-exception v1
+  001a: move-object v0, v1
+  001b: const/16 v1, #int 11 // #000b
+  001d: move v0, v1
+  001e: goto 0012 // -000c
+  001f: move-exception v1
+  0020: move-object v0, v1
+  0021: const/16 v1, #int 12 // #000c
+  0023: move v0, v1
+  0024: goto 0012 // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 001f
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 0006..0009
+      catch java.lang.NullPointerException -> 0013,
+        java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 0009..000c
+      catch java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 000c..000f
+      catch java.lang.RuntimeException -> 001f
+Blort.test4:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call5:()V
+  000c: const/16 v1, #int 13 // #000d
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 001c
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0006..0009
+      catch java.lang.NullPointerException -> 0010,
+        java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0009..000c
+      catch java.lang.RuntimeException -> 001c
+Blort.test5:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 13 // #000d
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  0019: move-exception v1
+  001a: move-object v0, v1
+  001b: const/16 v1, #int 12 // #000c
+  001d: move v0, v1
+  001e: goto 000c // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0019
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0013,
+        java.lang.RuntimeException -> 0019
+      try 0006..0009
+      catch java.lang.NullPointerException -> 000d,
+        java.lang.IndexOutOfBoundsException -> 0013,
+        java.lang.RuntimeException -> 0019
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 13 // #000d
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.NullPointerException -> 0010,
+        java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0006..0009
+      catch java.lang.RuntimeException -> 001c
+Blort.test7:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: const/16 v1, #int 12 // #000c
+  0008: move v0, v1
+  0009: return v0
+  000a: move-exception v1
+  000b: move-object v0, v1
+  000c: const/16 v1, #int 10 // #000a
+  000e: move v0, v1
+  000f: goto 0009 // -0006
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 11 // #000b
+  0014: move v0, v1
+  0015: goto 0009 // -000c
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 000a
+      try 0003..0006
+      catch java.lang.RuntimeException -> 0010
+Blort.test8:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 12 // #000c
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  catches
+    tries:
+      try 0000..0006
+      catch java.lang.RuntimeException -> 0010
+      try 0006..000c
+      catch java.lang.RuntimeException -> 0016
+Blort.test9:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 14 // #000e
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  0022: move-exception v1
+  0023: move-object v0, v1
+  0024: const/16 v1, #int 13 // #000d
+  0026: move v0, v1
+  0027: goto 000f // -0018
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0016
+      try 0003..0006
+      catch java.lang.IllegalArgumentException -> 0010,
+        java.lang.RuntimeException -> 0016
+      try 0006..0009
+      catch java.lang.RuntimeException -> 0022
+      try 0009..000c
+      catch java.lang.IllegalArgumentException -> 001c,
+        java.lang.RuntimeException -> 0022
diff --git a/dx/tests/080-dex-exception-tables/info.txt b/dx/tests/080-dex-exception-tables/info.txt
new file mode 100644
index 0000000..99f2cbc
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to make sure that
+exception handler tables get built reasonably (combining entries that
+ought to be combined, listing entries in a correct and sensible order,
+etc.).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/080-dex-exception-tables/run b/dx/tests/080-dex-exception-tables/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/081-dex-throws-list/Blort.java b/dx/tests/081-dex-throws-list/Blort.java
new file mode 100644
index 0000000..6011c9c
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+    public int test1()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test2()
+            throws Throwable, IllegalArgumentException {
+        throw new IllegalArgumentException();
+    }
+}
diff --git a/dx/tests/081-dex-throws-list/expected.txt b/dx/tests/081-dex-throws-list/expected.txt
new file mode 100644
index 0000000..502f724
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/expected.txt
@@ -0,0 +1,5 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:()I:
+    system-annotation dalvik.annotation.Throws {value: {java.lang.RuntimeException}}
+Blort.test2:()I:
+    system-annotation dalvik.annotation.Throws {value: {java.lang.Throwable, java.lang.IllegalArgumentException}}
diff --git a/dx/tests/081-dex-throws-list/info.txt b/dx/tests/081-dex-throws-list/info.txt
new file mode 100644
index 0000000..eb4bdd7
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+throws lists (that is, list of declared exceptions on methods) get
+represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/081-dex-throws-list/run b/dx/tests/081-dex-throws-list/run
new file mode 100644
index 0000000..2236cf2
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class | grep 'Blort\|Throws'
diff --git a/dx/tests/082-dex-throws-list-sharing/Blort.java b/dx/tests/082-dex-throws-list-sharing/Blort.java
new file mode 100644
index 0000000..31591d0
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/Blort.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+    public int test1()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test2()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test3()
+            throws Error, UnsupportedOperationException {
+        throw new RuntimeException();
+    }
+
+    public int test4()
+            throws Error, UnsupportedOperationException {
+        throw new RuntimeException();
+    }
+}
diff --git a/dx/tests/082-dex-throws-list-sharing/expected.txt b/dx/tests/082-dex-throws-list-sharing/expected.txt
new file mode 100644
index 0000000..016b41f
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/expected.txt
@@ -0,0 +1,3 @@
+javac 1.7.0-internal_bootstrap
+java.lang.RuntimeException
+java.lang.Error, java.lang.UnsupportedOperationException
diff --git a/dx/tests/082-dex-throws-list-sharing/info.txt b/dx/tests/082-dex-throws-list-sharing/info.txt
new file mode 100644
index 0000000..3b7dca1
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+identical throws lists in different methods get collapsed into a single
+dex file structure.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/082-dex-throws-list-sharing/run b/dx/tests/082-dex-throws-list-sharing/run
new file mode 100644
index 0000000..6eed9bd
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-to=- --dump-width=200 \
+    *.class | grep '^[0-9a-f].*value.*Exception' \
+    | sed -e 's/^[^{]*{//g' -e 's/}//g'
diff --git a/dx/tests/083-ssa-phi-placement/Blort.java b/dx/tests/083-ssa-phi-placement/Blort.java
new file mode 100644
index 0000000..b314290
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/Blort.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+
+    public int phiTest() {
+        int i = 1;
+        int j = 1;
+        int k = 0;
+
+        while (k < 100) { 
+            if (j < 20) {
+                j = i;
+                k++;
+            } else {
+                j = k;
+                k += 2;
+            }
+        }
+
+        return j;
+    }
+
+    /**
+     * This method uses no registers.
+     */
+    public static void noVars() {
+    }
+
+    /**
+     * This method requires an ordered successor list with
+     * multiple identically-valued entries.
+     */
+    Object fd;
+    public Object getOption(int optID) throws RuntimeException
+    {
+        if (fd == null) {
+            throw new RuntimeException("socket not created");
+        }
+
+        int value = 0;
+        switch (optID)
+        {
+            case 1:
+            case 2:
+                return new Integer(value);
+            case 3:
+            default:
+                return Boolean.valueOf(value != 0);
+        }
+    }
+}
+
diff --git a/dx/tests/083-ssa-phi-placement/expected.txt b/dx/tests/083-ssa-phi-placement/expected.txt
new file mode 100644
index 0000000..c02e439
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/expected.txt
@@ -0,0 +1,346 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 000a
+  live in:{}
+  Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+  y>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:17@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:17@0004: return-void . <- .
+  returns
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+
+method phiTest ()I
+first 0048
+block 0046
+  pred 0048
+  live in:{}
+  Blort.java:21@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:21@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0046
+  live in:{}
+  Blort.java:21@0000: const-int(1) v4:I=1 <- .
+  Blort.java:21@0001: move-int v1:I <- v4:I=1
+  Blort.java:22@0002: const-int(1) v4:I=1 <- .
+  Blort.java:22@0003: move-int v2:I <- v4:I=1
+  Blort.java:23@0004: const-int(0) v4:I=0 <- .
+  Blort.java:23@0005: move-int v3:I <- v4:I=0
+  Blort.java:23@0005: goto . <- .
+  next 0049
+  live out:{}
+block 0006
+  pred 0049
+  live in:{}
+  Blort.java:25@0006: move-int v4:I <- v3:I
+  Blort.java:25@0007: const-int(100) v5:I=100 <- .
+  Blort.java:25@0009: if-ge-int . <- v4:I v5:I=100
+  next 000c *
+  next 0022
+  live out:{}
+block 000c
+  pred 0006
+  live in:{}
+  Blort.java:26@000c: move-int v4:I <- v2:I
+  Blort.java:26@000d: const-int(20) v5:I=20 <- .
+  Blort.java:26@000f: if-ge-int . <- v4:I v5:I=20
+  next 0012 *
+  next 001a
+  live out:{}
+block 0012
+  pred 000c
+  live in:{}
+  Blort.java:27@0012: move-int v4:I <- v1:I
+  Blort.java:27@0013: move-int v2:I <- v4:I
+  Blort.java:28@0014: add-const-int(1) v3:I <- v3:I
+  Blort.java:28@0017: goto . <- .
+  next 0049
+  live out:{}
+block 001a
+  pred 000c
+  live in:{}
+  Blort.java:30@001a: move-int v4:I <- v3:I
+  Blort.java:30@001b: move-int v2:I <- v4:I
+  Blort.java:31@001c: add-const-int(2) v3:I <- v3:I
+  Blort.java:31@001f: goto . <- .
+  next 0049
+  live out:{}
+block 0022
+  pred 0006
+  live in:{}
+  Blort.java:35@0022: move-int v4:I <- v2:I
+  Blort.java:35@0023: move-int v0:I <- v4:I
+  Blort.java:35@0023: goto . <- .
+  next 0047
+  live out:{}
+block 0047
+  pred 0022
+  live in:{}
+  Blort.java:35@0023: return-int . <- v0:I
+  returns
+  live out:{}
+block 0048
+  live in:{}
+  @????: goto . <- .
+  next 0046
+  live out:{}
+block 0049
+  pred 0000
+  pred 0012
+  pred 001a
+  live in:{}
+  @????: phi v5:V <- .
+  @????: phi v4:V <- .
+  @????: phi v3:V <- .
+  @????: phi v2:V <- .
+  @????: goto . <- .
+  next 0006
+  live out:{}
+
+method noVars ()V
+first 0004
+block 0002
+  pred 0004
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0002
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0003
+  pred 0000
+  live in:{}
+  Blort.java:42@0000: return-void . <- .
+  returns
+  live out:{}
+block 0004
+  live in:{}
+  @????: goto . <- .
+  next 0002
+  live out:{}
+
+method getOption (I)Ljava/lang/Object;
+first 0098
+block 008c
+  pred 0098
+  live in:{}
+  Blort.java:51@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:51@0000: move-param-int(1) v1:I <- .
+  Blort.java:51@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0093
+  pred 0000
+  live in:{}
+  Blort.java:51@0001: Rop{move-result-pseudo Ljava/lang/Object; <- . flows} v3:
+  Ljava/lang/Object; <- .
+  Blort.java:51@0001: goto . <- .
+  next 0004
+  live out:{}
+block 0000
+  pred 008c
+  live in:{}
+  Blort.java:51@0000: move-object v3:LBlort; <- v0:LBlort;
+  Blort.java:51@0001: get-field-object(Blort.fd:Ljava/lang/Object; catch) . <- 
+  v3:LBlort;
+  next 0093
+  live out:{}
+block 0004
+  pred 0093
+  live in:{}
+  Blort.java:51@0004: if-nez-object . <- v3:Ljava/lang/Object;
+  next 0007 *
+  next 0011
+  live out:{}
+block 0094
+  pred 0007
+  live in:{}
+  Blort.java:52@0007: Rop{move-result-pseudo N0007Ljava/lang/RuntimeException; 
+  <- . flows} v3:N0007Ljava/lang/RuntimeException; <- .
+  Blort.java:52@0007: goto . <- .
+  next 000a
+  live out:{}
+block 0007
+  pred 0004
+  live in:{}
+  Blort.java:52@0007: new-instance(java.lang.RuntimeException catch) . <- .
+  next 0094
+  live out:{}
+block 0095
+  pred 000a
+  live in:{}
+  Blort.java:52@000b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v5:
+  Ljava/lang/String;="socket not created" <- .
+  Blort.java:52@000b: goto . <- .
+  next 000d
+  live out:{}
+block 000a
+  pred 0094
+  live in:{}
+  Blort.java:52@000a: move-object v6:N0007Ljava/lang/RuntimeException; <- v3:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000a: move-object v3:N0007Ljava/lang/RuntimeException; <- v6:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000a: move-object v4:N0007Ljava/lang/RuntimeException; <- v6:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000b: const-object("socket not created" catch) . <- .
+  next 0095
+  live out:{}
+block 000d
+  pred 0095
+  live in:{}
+  Blort.java:52@000d: Rop{invoke-direct . <- Ljava/lang/RuntimeException; Ljava
+  /lang/String; call throws <any>}(java.lang.RuntimeException.<init>:(Ljava/lan
+  g/String;)V catch) . <- v4:N0007Ljava/lang/RuntimeException; v5:Ljava/lang/St
+  ring;="socket not created"
+  next 0010
+  live out:{}
+block 0010
+  pred 000d
+  live in:{}
+  Blort.java:52@0010: throw(catch) . <- v3:Ljava/lang/RuntimeException;
+  returns
+  live out:{}
+block 0011
+  pred 0004
+  live in:{}
+  Blort.java:55@0011: const-int(0) v3:I=0 <- .
+  Blort.java:55@0012: move-int v2:I <- v3:I=0
+  Blort.java:56@0013: move-int v3:I <- v1:I
+  Blort.java:56@0014: switch({1, 2}) . <- v3:I
+  next 0030
+  next 0030
+  next 0039 *
+  live out:{}
+block 0096
+  pred 0030
+  live in:{}
+  Blort.java:60@0030: Rop{move-result-pseudo N0030Ljava/lang/Integer; <- . flow
+  s} v3:N0030Ljava/lang/Integer; <- .
+  Blort.java:60@0030: goto . <- .
+  next 0033
+  live out:{}
+block 0030
+  pred 0011
+  live in:{}
+  Blort.java:60@0030: new-instance(java.lang.Integer catch) . <- .
+  next 0096
+  live out:{}
+block 0033
+  pred 0096
+  live in:{}
+  Blort.java:60@0033: move-object v6:N0030Ljava/lang/Integer; <- v3:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0033: move-object v3:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0033: move-object v4:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0034: move-int v5:I <- v2:I
+  Blort.java:60@0035: Rop{invoke-direct . <- Ljava/lang/Integer; I call throws 
+  <any>}(java.lang.Integer.<init>:(I)V catch) . <- v4:N0030Ljava/lang/Integer; 
+  v5:I
+  next 0038
+  live out:{}
+block 0038
+  pred 0033
+  live in:{}
+  Blort.java:60@0038: move-object v0:Ljava/lang/Integer; <- v3:Ljava/lang/Integ
+  er;
+  Blort.java:60@0038: goto . <- .
+  next 008d
+  live out:{}
+block 0039
+  pred 0011
+  live in:{}
+  Blort.java:63@0039: move-int v3:I <- v2:I
+  Blort.java:63@003a: if-eqz-int . <- v3:I
+  next 003d *
+  next 0041
+  live out:{}
+block 003d
+  pred 0039
+  live in:{}
+  Blort.java:63@003d: const-int(1) v3:I=1 <- .
+  Blort.java:63@003e: goto . <- .
+  next 0042
+  live out:{}
+block 0041
+  pred 0039
+  live in:{}
+  Blort.java:63@0041: const-int(0) v3:I=0 <- .
+  Blort.java:63@0041: goto . <- .
+  next 0042
+  live out:{}
+block 0097
+  pred 0042
+  live in:{}
+  Blort.java:63@0042: Rop{move-result Ljava/lang/Boolean; <- . flows} v3:Ljava/
+  lang/Boolean; <- .
+  Blort.java:63@0042: goto . <- .
+  next 0045
+  live out:{}
+block 0042
+  pred 003d
+  pred 0041
+  live in:{}
+  @????: phi v3:V <- .
+  Blort.java:63@0042: Rop{invoke-static . <- I call throws <any>}(java.lang.Boo
+  lean.valueOf:(Z)Ljava/lang/Boolean; catch) . <- v3:I
+  next 0097
+  live out:{}
+block 0045
+  pred 0097
+  live in:{}
+  Blort.java:63@0045: move-object v0:Ljava/lang/Boolean; <- v3:Ljava/lang/Boole
+  an;
+  Blort.java:63@0045: goto . <- .
+  next 008d
+  live out:{}
+block 008d
+  pred 0038
+  pred 0045
+  live in:{}
+  @????: phi v6:V <- .
+  @????: phi v5:V <- .
+  @????: phi v4:V <- .
+  @????: phi v3:V <- .
+  @????: phi v0:V <- .
+  Blort.java:63@0045: return-object . <- v0:Ljava/lang/Object;
+  returns
+  live out:{}
+block 0098
+  live in:{}
+  @????: goto . <- .
+  next 008c
+  live out:{}
diff --git a/dx/tests/083-ssa-phi-placement/info.txt b/dx/tests/083-ssa-phi-placement/info.txt
new file mode 100644
index 0000000..8d4eebf
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the phi placement algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/083-ssa-phi-placement/run b/dx/tests/083-ssa-phi-placement/run
new file mode 100644
index 0000000..ddcb597
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --ssa-blocks --ssa-step=phi-placement Blort.class
diff --git a/dx/tests/084-dex-high-register-moves/Blort.java b/dx/tests/084-dex-high-register-moves/Blort.java
new file mode 100644
index 0000000..736cefb
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/Blort.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    private static int i;
+    private static long l;
+    private static Object o;
+    
+    public static void test() {
+        int i0 = 0;
+        int i1 = 0;
+        int i2 = 0;
+        int i3 = 0;
+        int i4 = 0;
+        int i5 = 0;
+        int i6 = 0;
+        int i7 = 0;
+        int i8 = 0;
+        int i9 = 0;
+        int i10 = 0;
+        int i11 = 0;
+        int i12 = 0;
+        int i13 = 0;
+        int i14 = 0;
+        int i15 = 0;
+
+        int ix = i;
+        long lx = l;
+        Object ox = o;
+
+        i = -ix;
+        l = -lx;
+        i = (ox instanceof String) ? 0 : 1;
+    }
+}
diff --git a/dx/tests/084-dex-high-register-moves/expected.txt b/dx/tests/084-dex-high-register-moves/expected.txt
new file mode 100644
index 0000000..e24cde0
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/expected.txt
@@ -0,0 +1,61 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()V:
+regs: 0018; ins: 0000; outs: 0000
+  0000: const/16 v22, #int 0 // #0000
+  0002: move/from16 v2, v22
+  0004: const/16 v22, #int 0 // #0000
+  0006: move/from16 v3, v22
+  0008: const/16 v22, #int 0 // #0000
+  000a: move/from16 v4, v22
+  000c: const/16 v22, #int 0 // #0000
+  000e: move/from16 v5, v22
+  0010: const/16 v22, #int 0 // #0000
+  0012: move/from16 v6, v22
+  0014: const/16 v22, #int 0 // #0000
+  0016: move/from16 v7, v22
+  0018: const/16 v22, #int 0 // #0000
+  001a: move/from16 v8, v22
+  001c: const/16 v22, #int 0 // #0000
+  001e: move/from16 v9, v22
+  0020: const/16 v22, #int 0 // #0000
+  0022: move/from16 v10, v22
+  0024: const/16 v22, #int 0 // #0000
+  0026: move/from16 v11, v22
+  0028: const/16 v22, #int 0 // #0000
+  002a: move/from16 v12, v22
+  002c: const/16 v22, #int 0 // #0000
+  002e: move/from16 v13, v22
+  0030: const/16 v22, #int 0 // #0000
+  0032: move/from16 v14, v22
+  0034: const/16 v22, #int 0 // #0000
+  0036: move/from16 v15, v22
+  0038: const/16 v22, #int 0 // #0000
+  003a: move/from16 v16, v22
+  003c: const/16 v22, #int 0 // #0000
+  003e: move/from16 v17, v22
+  0040: sget v22, Blort.i:I
+  0042: move/from16 v18, v22
+  0044: sget-wide v22, Blort.l:J
+  0046: move-wide/from16 v19, v22
+  0048: sget-object v22, Blort.o:Ljava/lang/Object;
+  004a: move-object/from16 v21, v22
+  004c: move/from16 v22, v18
+  004e: move/from16 v0, v22
+  0050: neg-int v0, v0
+  0051: move/from16 v22, v0
+  0053: sput v22, Blort.i:I
+  0055: move-wide/from16 v22, v19
+  0057: move-wide/from16 v0, v22
+  0059: neg-long v0, v0
+  005a: move-wide/from16 v22, v0
+  005c: sput-wide v22, Blort.l:J
+  005e: move-object/from16 v22, v21
+  0060: move-object/from16 v0, v22
+  0062: instance-of v0, v0, java.lang.String
+  0064: move/from16 v22, v0
+  0066: if-eqz v22, 006d // +0007
+  0068: const/16 v22, #int 0 // #0000
+  006a: sput v22, Blort.i:I
+  006c: return-void
+  006d: const/16 v22, #int 1 // #0001
+  006f: goto 006a // -0005
diff --git a/dx/tests/084-dex-high-register-moves/info.txt b/dx/tests/084-dex-high-register-moves/info.txt
new file mode 100644
index 0000000..77f0945
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+high registers are moved to and from low registers with
+type-appropriate instructions.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/084-dex-high-register-moves/run b/dx/tests/084-dex-high-register-moves/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/085-dex-jsr-ret/blort.j b/dx/tests/085-dex-jsr-ret/blort.j
new file mode 100644
index 0000000..2f4bf38
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/blort.j
@@ -0,0 +1,71 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    dup
+    dup
+    astore_1
+    pop2
+    return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()Ljava/lang/Object;
+    .limit locals 3
+    .limit stack 4
+    aload_0
+    jsr j1
+    aload_0
+    pop
+    ; Call j1 with different locals
+    ldc 10
+    astore_0
+    jsr j1
+    aload_0
+    pop
+    jsr j3
+    areturn
+j1:
+    astore_2
+    jsr_w j2
+    ret 2
+j2:
+    ; a subroutine with two returns and a catch block
+    astore_1
+    dup
+    dup
+    ; Just something that could throw an exception...
+    invokevirtual blort.test_jsr()V
+    ifnonnull j2a
+    ret_w 1
+j2a:
+    ret_w 1
+j3:
+    ; a subroutine that does not return
+    pop
+    areturn
+catchBlock:
+    areturn
+
+.catch java/lang/Throwable from j2 to j2a using catchBlock
+.end method
+
+
diff --git a/dx/tests/085-dex-jsr-ret/expected.txt b/dx/tests/085-dex-jsr-ret/expected.txt
new file mode 100644
index 0000000..ba61996
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/expected.txt
@@ -0,0 +1,171 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+first 0002
+block 0002
+  blort.j:@0000: move-param-object(0) v0:NffffLblort; <- .
+  blort.j:@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0002
+  blort.j:@0000: move-object v2:NffffLblort; <- v0:NffffLblort;
+  blort.j:@0001: move-object v5:NffffLblort; <- v2:NffffLblort;
+  blort.j:@0001: move-object v2:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0001: move-object v3:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0002: move-object v5:NffffLblort; <- v3:NffffLblort;
+  blort.j:@0002: move-object v3:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0002: move-object v4:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0003: move-object v1:NffffLblort; <- v4:NffffLblort;
+  blort.j:@0005: goto . <- .
+  next 0003
+block 0003
+  pred 0000
+  blort.j:@0005: return-void . <- .
+  returns
+
+method test_jsr ()Ljava/lang/Object;
+first 005c
+block 005c
+  blort.j:@0000: move-param-object(0) v0:Lblort; <- .
+  blort.j:@0000: goto . <- .
+  next 0000
+block 0000
+  pred 005c
+  blort.j:@0000: move-object v3:Lblort; <- v0:Lblort;
+  blort.j:@0000: goto . <- .
+  next 0001
+block 0004
+  pred 0065
+  blort.j:@0004: move-object v4:Lblort; <- v0:Lblort;
+  blort.j:@0006: const-int(10) v4:I=10 <- .
+  blort.j:@0008: move-int v0:I=10 <- v4:I=10
+  blort.j:@0008: goto . <- .
+  next 0009
+block 000c
+  pred 006e
+  blort.j:@000c: move-int v4:I=10 <- v0:I=10
+  blort.j:@000c: goto . <- .
+  next 000e
+block 005d
+  pred 006b
+  pred 0074
+  pred 0075
+  blort.j:@002c: return-object . <- v0:Ljava/lang/Object;
+  returns
+block 0063
+  pred 0001
+  blort.j:@0012: goto . <- .
+  next 0064
+block 0066
+  pred 0064
+  blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+  blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+  blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+  blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+  blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+  jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+  next 0067
+  next 0068 *
+block 0068
+  pred 0066
+  blort.j:@0020: if-nez-object . <- v4:Lblort;
+  next 0069 *
+  next 006a
+block 0069
+  pred 0068
+  @????: goto . <- .
+  next 0065
+block 006a
+  pred 0068
+  @????: goto . <- .
+  next 0065
+block 0067
+  pred 0066
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 006b
+block 006b
+  pred 0067
+  blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+  va/lang/Class;=java.lang.Throwable
+  blort.j:@002d: goto . <- .
+  next 005d
+block 0064
+  pred 0063
+  @????: goto . <- .
+  next 0066
+block 0065
+  pred 0069
+  pred 006a
+  @????: goto . <- .
+  next 0004
+block 0001
+  pred 0000
+  @????: goto . <- .
+  next 0063
+block 006c
+  pred 0009
+  blort.j:@0012: goto . <- .
+  next 006d
+block 006f
+  pred 006d
+  blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+  blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+  blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+  blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+  blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+  jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+  next 0070
+  next 0071 *
+block 0071
+  pred 006f
+  blort.j:@0020: if-nez-object . <- v4:Lblort;
+  next 0072 *
+  next 0073
+block 0072
+  pred 0071
+  @????: goto . <- .
+  next 006e
+block 0073
+  pred 0071
+  @????: goto . <- .
+  next 006e
+block 0070
+  pred 006f
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 0074
+block 0074
+  pred 0070
+  blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+  va/lang/Class;=java.lang.Throwable
+  blort.j:@002d: goto . <- .
+  next 005d
+block 006d
+  pred 006c
+  @????: goto . <- .
+  next 006f
+block 006e
+  pred 0072
+  pred 0073
+  @????: goto . <- .
+  next 000c
+block 0009
+  pred 0004
+  @????: goto . <- .
+  next 006c
+block 0075
+  pred 000e
+  blort.j:@002c: move-object v0:Lblort; <- v3:Lblort;
+  blort.j:@002c: goto . <- .
+  next 005d
+block 000e
+  pred 000c
+  @????: goto . <- .
+  next 0075
diff --git a/dx/tests/085-dex-jsr-ret/info.txt b/dx/tests/085-dex-jsr-ret/info.txt
new file mode 100644
index 0000000..4542fde
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/info.txt
@@ -0,0 +1,2 @@
+Tests handling of the Java jsr/jsr_w/ret bytecodes.
+
diff --git a/dx/tests/085-dex-jsr-ret/run b/dx/tests/085-dex-jsr-ret/run
new file mode 100644
index 0000000..00a7404
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --dump --rop-blocks blort.class
diff --git a/dx/tests/086-ssa-edge-split/Blort.java b/dx/tests/086-ssa-edge-split/Blort.java
new file mode 100644
index 0000000..5e54dfd
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/Blort.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    /**
+     * This method requires the edge-splitter to add a node
+     * to get to the finally block, since there are
+     * two exception sources.
+     * 
+     */
+    public int edgeSplitPredTest(int x) {
+        int y = 1;
+
+        try {
+            Integer.toString(x);
+            Integer.toString(x);
+            y++;
+        } finally {
+            return y;
+        }
+    }
+
+    /**
+     * just because this should do nothing
+     */
+    void voidFunction() {
+    }
+
+    /**
+     * Current SSA form requires each move-exception block to have
+     * a unique predecessor
+     */
+    void edgeSplitMoveException() {
+        try { 
+            hashCode();
+            hashCode();
+        } catch (Throwable tr) {
+        }
+    }
+
+    /**
+     * Presently, any basic block ending in an instruction with
+     * a result needs to have a unique successor. This appies
+     * only to the block between the switch instruction and the return
+     * in this case.
+     */
+    int edgeSplitSuccessor(int x) {
+        int y = 0;
+        
+        switch(x) {
+            case 1: y++;
+            break;
+            case 2: y++;
+            break;
+            case 3: y++;
+            break;
+        }
+        return y;
+    }
+}
+
diff --git a/dx/tests/086-ssa-edge-split/expected.txt b/dx/tests/086-ssa-edge-split/expected.txt
new file mode 100644
index 0000000..bc29b20
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/expected.txt
@@ -0,0 +1,344 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 000a
+  live in:{}
+  Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:17@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:17@0004: return-void . <- .
+  returns
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+
+method edgeSplitPredTest (I)I
+first 002f
+block 0026
+  pred 002f
+  live in:{}
+  Blort.java:26@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:26@0000: move-param-int(1) v1:I <- .
+  Blort.java:26@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0026
+  live in:{}
+  Blort.java:26@0000: const-int(1) v4:I=1 <- .
+  Blort.java:26@0001: move-int v2:I <- v4:I=1
+  Blort.java:26@0001: goto . <- .
+  next 0002
+  live out:{}
+block 002d
+  pred 0002
+  live in:{}
+  Blort.java:29@0003: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+  Blort.java:29@0003: goto . <- .
+  next 0006
+  live out:{}
+block 0002
+  pred 0000
+  live in:{}
+  Blort.java:29@0002: move-int v4:I <- v1:I
+  Blort.java:29@0003: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+  next 0030
+  next 002d *
+  live out:{}
+block 002e
+  pred 0006
+  live in:{}
+  Blort.java:30@0008: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+  Blort.java:30@0008: goto . <- .
+  next 000b
+  live out:{}
+block 0006
+  pred 002d
+  live in:{}
+  Blort.java:30@0007: move-int v4:I <- v1:I
+  Blort.java:30@0008: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+  next 0031
+  next 002e *
+  live out:{}
+block 000b
+  pred 002e
+  live in:{}
+  Blort.java:31@000c: add-const-int(1) v2:I <- v2:I
+  Blort.java:31@000c: goto . <- .
+  next 000f
+  live out:{}
+block 000f
+  pred 000b
+  live in:{}
+  Blort.java:33@000f: move-int v4:I <- v2:I
+  Blort.java:33@0010: move-int v0:I <- v4:I
+  Blort.java:33@0010: goto . <- .
+  next 0027
+  live out:{}
+block 0011
+  pred 0024
+  live in:{}
+  Blort.java:33@0011: move-object v3:Ljava/lang/Class;=java.lang.Object <- v4:Ljava/lang/Class;=java.lang.Object
+  Blort.java:33@0011: goto . <- .
+  next 0012
+  live out:{}
+block 0012
+  pred 0011
+  live in:{}
+  Blort.java:33@0012: move-int v4:I <- v2:I
+  Blort.java:33@0013: move-int v0:I <- v4:I
+  Blort.java:33@0013: goto . <- .
+  next 0027
+  live out:{}
+block 0027
+  pred 000f
+  pred 0012
+  live in:{}
+  Blort.java:33@0010: return-int . <- v0:I
+  returns
+  live out:{}
+block 0024
+  pred 0030
+  pred 0031
+  live in:{}
+  Blort.java:33@0011: goto . <- .
+  next 0011
+  live out:{}
+block 002f
+  live in:{}
+  @????: goto . <- .
+  next 0026
+  live out:{}
+block 0030
+  pred 0002
+  live in:{}
+  Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+  @????: goto . <- .
+  next 0024
+  live out:{}
+block 0031
+  pred 0006
+  live in:{}
+  Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+  @????: goto . <- .
+  next 0024
+  live out:{}
+
+method voidFunction ()V
+first 0004
+block 0002
+  pred 0004
+  live in:{}
+  Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:41@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0002
+  live in:{}
+  Blort.java:41@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0003
+  pred 0000
+  live in:{}
+  Blort.java:41@0000: return-void . <- .
+  returns
+  live out:{}
+block 0004
+  live in:{}
+  @????: goto . <- .
+  next 0002
+  live out:{}
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+  pred 0027
+  live in:{}
+  Blort.java:49@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:49@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0025
+  pred 0000
+  live in:{}
+  Blort.java:49@0001: Rop{move-result I <- . flows} v2:I <- .
+  Blort.java:49@0001: goto . <- .
+  next 0004
+  live out:{}
+block 0000
+  pred 001e
+  live in:{}
+  Blort.java:49@0000: move-object v2:LBlort; <- v0:LBlort;
+  Blort.java:49@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+  next 0028
+  next 0025 *
+  live out:{}
+block 0026
+  pred 0004
+  live in:{}
+  Blort.java:50@0006: Rop{move-result I <- . flows} v2:I <- .
+  Blort.java:50@0006: goto . <- .
+  next 0009
+  live out:{}
+block 0004
+  pred 0025
+  live in:{}
+  Blort.java:50@0005: move-object v2:LBlort; <- v0:LBlort;
+  Blort.java:50@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+  next 0029
+  next 0026 *
+  live out:{}
+block 0009
+  pred 0026
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+block 000a
+  pred 0009
+  live in:{}
+  Blort.java:52@000a: goto . <- .
+  next 000e
+  live out:{}
+block 000d
+  pred 001c
+  live in:{}
+  Blort.java:51@000d: move-object v1:Ljava/lang/Class;=java.lang.Throwable <- v2:Ljava/lang/Class;=java.lang.Throwable
+  Blort.java:51@000d: goto . <- .
+  next 000e
+  live out:{}
+block 000e
+  pred 000a
+  pred 000d
+  live in:{}
+  Blort.java:53@000e: goto . <- .
+  next 001f
+  live out:{}
+block 001f
+  pred 000e
+  live in:{}
+  Blort.java:53@000e: return-void . <- .
+  returns
+  live out:{}
+block 001c
+  pred 0028
+  pred 0029
+  live in:{}
+  Blort.java:51@000d: goto . <- .
+  next 000d
+  live out:{}
+block 0027
+  live in:{}
+  @????: goto . <- .
+  next 001e
+  live out:{}
+block 0028
+  pred 0000
+  live in:{}
+  Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001c
+  live out:{}
+block 0029
+  pred 0004
+  live in:{}
+  Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001c
+  live out:{}
+
+method edgeSplitSuccessor (I)I
+first 005a
+block 0058
+  pred 005a
+  live in:{}
+  Blort.java:62@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:62@0000: move-param-int(1) v1:I <- .
+  Blort.java:62@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0058
+  live in:{}
+  Blort.java:62@0000: const-int(0) v3:I=0 <- .
+  Blort.java:62@0001: move-int v2:I <- v3:I=0
+  Blort.java:64@0002: move-int v3:I <- v1:I
+  Blort.java:64@0003: switch({1, 2, 3}) . <- v3:I
+  next 001c
+  next 0022
+  next 0028
+  next 005b *
+  live out:{}
+block 001c
+  pred 0000
+  live in:{}
+  Blort.java:65@001c: add-const-int(1) v2:I <- v2:I
+  Blort.java:66@001f: goto . <- .
+  next 002b
+  live out:{}
+block 0022
+  pred 0000
+  live in:{}
+  Blort.java:67@0022: add-const-int(1) v2:I <- v2:I
+  Blort.java:68@0025: goto . <- .
+  next 002b
+  live out:{}
+block 0028
+  pred 0000
+  live in:{}
+  Blort.java:69@0028: add-const-int(1) v2:I <- v2:I
+  Blort.java:69@0028: goto . <- .
+  next 002b
+  live out:{}
+block 002b
+  pred 001c
+  pred 0022
+  pred 0028
+  pred 005b
+  live in:{}
+  Blort.java:72@002b: move-int v3:I <- v2:I
+  Blort.java:72@002c: move-int v0:I <- v3:I
+  Blort.java:72@002c: goto . <- .
+  next 0059
+  live out:{}
+block 0059
+  pred 002b
+  live in:{}
+  Blort.java:72@002c: return-int . <- v0:I
+  returns
+  live out:{}
+block 005a
+  live in:{}
+  @????: goto . <- .
+  next 0058
+  live out:{}
+block 005b
+  pred 0000
+  live in:{}
+  @????: goto . <- .
+  next 002b
+  live out:{}
diff --git a/dx/tests/086-ssa-edge-split/info.txt b/dx/tests/086-ssa-edge-split/info.txt
new file mode 100644
index 0000000..ff873e8
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the edge-splitting algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/086-ssa-edge-split/run b/dx/tests/086-ssa-edge-split/run
new file mode 100644
index 0000000..914deb1
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --ssa-blocks --ssa-step=edge-split Blort.class
diff --git a/dx/tests/087-ssa-local-vars/Blort.java b/dx/tests/087-ssa-local-vars/Blort.java
new file mode 100644
index 0000000..f149790
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/Blort.java
@@ -0,0 +1,95 @@
+import java.io.IOException;
+class Blort {
+    private static void arrayCopyTest(int k) {
+        // A local variable assigned from an argument
+        int j = k;
+        // These two locals are defined once and used multiple times
+        String[] stringArray = new String[8];
+        Object[] objectArray = new Object[8];
+        // Should cause another move to be inserted
+        Object anotherOne = objectArray;
+
+        if (anotherOne != null) {
+            System.out.println("foo");
+        }
+
+        // "i" is used in a loop
+        for (int i = 0; i < stringArray.length; i++)
+            stringArray[i] = new String(Integer.toString(i));
+
+        System.out.println("string -> object");
+        System.arraycopy(stringArray, 0, objectArray, 0, stringArray.length);
+        System.out.println("object -> string");
+        System.arraycopy(objectArray, 0, stringArray, 0, stringArray.length);
+        System.out.println("object -> string (modified)");
+        objectArray[4] = new Object();
+        try {
+            System.arraycopy(objectArray, 0, stringArray, 0,stringArray.length);        
+        } catch (ArrayStoreException ase) {
+            // "ase" is an unused local which still must be preserved
+            System.out.println("caught ArrayStoreException (expected)");
+        }
+    }
+
+    private void testConstructor() {
+        Blort foo = null;
+        try {
+            foo = new Blort();
+        } catch (Exception ex) {
+        }
+        System.err.println(foo);
+    }
+    /**
+     * Stolen from 
+     * java/android/org/apache/http/impl/io/AbstractMessageParser.java
+     * Simplified.
+     *
+     * Checks to see that local variable assignment is preserved through
+     * phi's. The key component here is the assignment of previous = current.
+     */
+    public static void parseHeaderGroup(
+            final Object headGroup,
+            final Object inbuffer,
+            int maxHeaderCount,
+            int maxLineLen)
+        throws  IOException {
+
+        StringBuilder current = null;
+        StringBuilder previous = null;
+        for (;;) {
+            if (current == null) {
+                current = new StringBuilder(64);
+            } else {
+                current.length();
+            }
+            int l = inbuffer.hashCode();
+            if (l == -1 || current.length() < 1) {
+                break;
+            }
+
+            if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
+                int i = 0;
+                while (i < current.length()) {
+                    char ch = current.charAt(i);
+                    if (ch != ' ' && ch != '\t') {
+                        break;
+                    }
+                    i++;
+                }
+                if (maxLineLen > 0 
+                        && previous.length() + 1 + current.length() - i > maxLineLen) {
+                    throw new IOException("Maximum line length limit exceeded");
+                }
+                previous.append(' ');
+                previous.append(current, i, current.length() - i);
+            } else {
+                previous = current;
+                current = null;
+            }
+            if (maxHeaderCount > 0) {
+                throw new IOException("Maximum header count exceeded");
+            }
+        }
+    }
+}
+
diff --git a/dx/tests/087-ssa-local-vars/expected.txt b/dx/tests/087-ssa-local-vars/expected.txt
new file mode 100644
index 0000000..9d8f81f
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/expected.txt
@@ -0,0 +1,1267 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:2@0000: move-param-object(0) v2:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+  live out:{2}
+block 0000
+  pred 000a
+  live in:{2}
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v2:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:2@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:2@0004: return-void . <- .
+  next 000d
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+block 000d
+  pred 000b
+  live in:{}
+  returns
+  live out:{}
+
+method arrayCopyTest (I)V
+first 012c
+block 0112
+  pred 012c
+  live in:{62, 63}
+  Blort.java:5@0000: move-param-int(0) v12:"k"I <- .
+  Blort.java:5@0000: goto . <- .
+  next 0000
+  live out:{12, 62, 63}
+block 0119
+  pred 0000
+  live in:{62, 63}
+  Blort.java:7@0004: Rop{move-result-pseudo [Ljava/lang/String; <- . flows} v15
+  :[Ljava/lang/String; <- .
+  Blort.java:7@0004: goto . <- .
+  next 0007
+  live out:{15, 62, 63}
+block 0000
+  pred 0112
+  live in:{12, 62, 63}
+  Blort.java:5@0001: move-int v13:"j"I <- v12:I
+  Blort.java:7@0004: new-array-object(java.lang.String[] catch) . <- v63:I=8
+  next 0119
+  live out:{62, 63}
+block 011a
+  pred 0007
+  live in:{15, 62}
+  Blort.java:8@000a: Rop{move-result-pseudo [Ljava/lang/Object; <- . flows} v17
+  :[Ljava/lang/Object; <- .
+  Blort.java:8@000a: goto . <- .
+  next 000d
+  live out:{15, 17, 62}
+block 0007
+  pred 0119
+  live in:{15, 62, 63}
+  @????: mark-local-object . <- v15:"stringArray"[Ljava/lang/String;
+  Blort.java:8@000a: new-array-object(java.lang.Object[] catch) . <- v63:I=8
+  next 011a
+  live out:{15, 62}
+block 000d
+  pred 011a
+  live in:{15, 17, 62}
+  @????: mark-local-object . <- v17:"objectArray"[Ljava/lang/Object;
+  Blort.java:10@000f: move-object v18:"anotherOne"[Ljava/lang/Object; <- v17:[L
+  java/lang/Object;
+  Blort.java:12@0013: if-eqz-object . <- v18:[Ljava/lang/Object;
+  next 0016 *
+  next 0131
+  live out:{15, 17, 62}
+block 011b
+  pred 0016
+  live in:{15, 17, 62}
+  Blort.java:13@0016: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v19:Ljava/io/PrintStream; <- .
+  Blort.java:13@0016: goto . <- .
+  next 0019
+  live out:{15, 17, 19, 62}
+block 0016
+  pred 000d
+  live in:{15, 17, 62}
+  Blort.java:13@0016: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 011b
+  live out:{15, 17, 62}
+block 011c
+  pred 0019
+  live in:{15, 17, 19, 62}
+  Blort.java:13@0019: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v20
+  :Ljava/lang/String;="foo" <- .
+  Blort.java:13@0019: goto . <- .
+  next 001b
+  live out:{15, 17, 19, 20, 62}
+block 0019
+  pred 011b
+  live in:{15, 17, 19, 62}
+  Blort.java:13@0019: const-object("foo" catch) . <- .
+  next 011c
+  live out:{15, 17, 19, 62}
+block 001b
+  pred 011c
+  live in:{15, 17, 19, 20, 62}
+  Blort.java:13@001b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v19:Ljava/io/PrintStream; v20:Ljava/lang/String;="foo"
+  next 0130
+  live out:{15, 17, 62}
+block 001e
+  pred 0130
+  pred 0131
+  live in:{15, 17, 62}
+  Blort.java:17@001e: const-int(0) v23:I=0 <- .
+  @????: mark-local-int . <- v23:"i"I
+  Blort.java:17@001f: goto . <- .
+  next 0021
+  live out:{15, 17, 23, 62}
+block 011d
+  pred 0021
+  live in:{15, 17, 30, 62}
+  Blort.java:17@0024: Rop{move-result-pseudo I <- . flows} v31:I <- .
+  Blort.java:17@0024: goto . <- .
+  next 0025
+  live out:{15, 17, 30, 31, 62}
+block 0021
+  pred 001e
+  pred 0038
+  live in:{15, 17, 62}
+  @????: phi v30:"i"I <- v23:"i"I[b=001e] v34:"i"I[b=0038]
+  Blort.java:17@0024: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 011d
+  live out:{15, 17, 30, 62}
+block 0025
+  pred 011d
+  live in:{15, 17, 30, 31, 62}
+  Blort.java:17@0025: if-ge-int . <- v30:I v31:I
+  next 0028 *
+  next 003e
+  live out:{15, 17, 30, 62}
+block 011e
+  pred 0028
+  live in:{15, 17, 30, 62}
+  Blort.java:18@002b: Rop{move-result-pseudo N002bLjava/lang/String; <- . flows
+  } v32:N002bLjava/lang/String; <- .
+  Blort.java:18@002b: goto . <- .
+  next 002e
+  live out:{15, 17, 30, 32, 62}
+block 0028
+  pred 0025
+  live in:{15, 17, 30, 62}
+  Blort.java:18@002b: new-instance(java.lang.String catch) . <- .
+  next 011e
+  live out:{15, 17, 30, 62}
+block 011f
+  pred 002e
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0031: Rop{move-result Ljava/lang/String; <- . flows} v33:Ljava/
+  lang/String; <- .
+  Blort.java:18@0031: goto . <- .
+  next 0034
+  live out:{15, 17, 30, 32, 33, 62}
+block 002e
+  pred 011e
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0031: Rop{invoke-static . <- I call throws <any>}(java.lang.Int
+  eger.toString:(I)Ljava/lang/String; catch) . <- v30:I
+  next 011f
+  live out:{15, 17, 30, 32, 62}
+block 0034
+  pred 011f
+  live in:{15, 17, 30, 32, 33, 62}
+  Blort.java:18@0034: Rop{invoke-direct . <- Ljava/lang/String; Ljava/lang/Stri
+  ng; call throws <any>}(java.lang.String.<init>:(Ljava/lang/String;)V catch) .
+   <- v32:N002bLjava/lang/String; v33:Ljava/lang/String;
+  next 0037
+  live out:{15, 17, 30, 32, 62}
+block 0037
+  pred 0034
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0037: aput-object(catch) . <- v32:Ljava/lang/String; v15:[Ljava
+  /lang/String; v30:I
+  next 0038
+  live out:{15, 17, 30, 62}
+block 0038
+  pred 0037
+  live in:{15, 17, 30, 62}
+  Blort.java:17@0038: add-const-int(1) v34:"i"I <- v30:I
+  Blort.java:17@003b: goto . <- .
+  next 0021
+  live out:{15, 17, 34, 62}
+block 0120
+  pred 003e
+  live in:{15, 17, 62}
+  Blort.java:20@003e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v35:Ljava/io/PrintStream; <- .
+  Blort.java:20@003e: goto . <- .
+  next 0041
+  live out:{15, 17, 35, 62}
+block 003e
+  pred 0025
+  live in:{15, 17, 62}
+  Blort.java:20@003e: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0120
+  live out:{15, 17, 62}
+block 0121
+  pred 0041
+  live in:{15, 17, 35, 62}
+  Blort.java:20@0041: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v36
+  :Ljava/lang/String;="string -> object" <- .
+  Blort.java:20@0041: goto . <- .
+  next 0043
+  live out:{15, 17, 35, 36, 62}
+block 0041
+  pred 0120
+  live in:{15, 17, 35, 62}
+  Blort.java:20@0041: const-object("string -> object" catch) . <- .
+  next 0121
+  live out:{15, 17, 35, 62}
+block 0043
+  pred 0121
+  live in:{15, 17, 35, 36, 62}
+  Blort.java:20@0043: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v35:Ljava/io/PrintStream; v36:Ljava/lang/String;="string -> objec
+  t"
+  next 0046
+  live out:{15, 17, 62}
+block 0122
+  pred 0046
+  live in:{15, 17, 62}
+  Blort.java:21@004b: Rop{move-result-pseudo I <- . flows} v39:I <- .
+  Blort.java:21@004b: goto . <- .
+  next 004c
+  live out:{15, 17, 39, 62}
+block 0046
+  pred 0043
+  live in:{15, 17, 62}
+  Blort.java:21@004b: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 0122
+  live out:{15, 17, 62}
+block 004c
+  pred 0122
+  live in:{15, 17, 39, 62}
+  Blort.java:21@004c: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch) . <- v15:[Ljava/lang/String; v62:I=0 v17:[Ljava/
+  lang/Object; v62:I=0 v39:I
+  next 004f
+  live out:{15, 17, 62}
+block 0123
+  pred 004f
+  live in:{15, 17, 62}
+  Blort.java:22@004f: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v40:Ljava/io/PrintStream; <- .
+  Blort.java:22@004f: goto . <- .
+  next 0052
+  live out:{15, 17, 40, 62}
+block 004f
+  pred 004c
+  live in:{15, 17, 62}
+  Blort.java:22@004f: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0123
+  live out:{15, 17, 62}
+block 0124
+  pred 0052
+  live in:{15, 17, 40, 62}
+  Blort.java:22@0052: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v41
+  :Ljava/lang/String;="object -> string" <- .
+  Blort.java:22@0052: goto . <- .
+  next 0054
+  live out:{15, 17, 40, 41, 62}
+block 0052
+  pred 0123
+  live in:{15, 17, 40, 62}
+  Blort.java:22@0052: const-object("object -> string" catch) . <- .
+  next 0124
+  live out:{15, 17, 40, 62}
+block 0054
+  pred 0124
+  live in:{15, 17, 40, 41, 62}
+  Blort.java:22@0054: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v40:Ljava/io/PrintStream; v41:Ljava/lang/String;="object -> strin
+  g"
+  next 0057
+  live out:{15, 17, 62}
+block 0125
+  pred 0057
+  live in:{15, 17, 62}
+  Blort.java:23@005c: Rop{move-result-pseudo I <- . flows} v44:I <- .
+  Blort.java:23@005c: goto . <- .
+  next 005d
+  live out:{15, 17, 44, 62}
+block 0057
+  pred 0054
+  live in:{15, 17, 62}
+  Blort.java:23@005c: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 0125
+  live out:{15, 17, 62}
+block 005d
+  pred 0125
+  live in:{15, 17, 44, 62}
+  Blort.java:23@005d: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch) . <- v17:[Ljava/lang/Object; v62:I=0 v15:[Ljava/
+  lang/String; v62:I=0 v44:I
+  next 0060
+  live out:{15, 17}
+block 0126
+  pred 0060
+  live in:{15, 17}
+  Blort.java:24@0060: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v45:Ljava/io/PrintStream; <- .
+  Blort.java:24@0060: goto . <- .
+  next 0063
+  live out:{15, 17, 45}
+block 0060
+  pred 005d
+  live in:{15, 17}
+  Blort.java:24@0060: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0126
+  live out:{15, 17}
+block 0127
+  pred 0063
+  live in:{15, 17, 45}
+  Blort.java:24@0063: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v46
+  :Ljava/lang/String;="object -> string (modified)" <- .
+  Blort.java:24@0063: goto . <- .
+  next 0065
+  live out:{15, 17, 45, 46}
+block 0063
+  pred 0126
+  live in:{15, 17, 45}
+  Blort.java:24@0063: const-object("object -> string (modified)" catch) . <- .
+  next 0127
+  live out:{15, 17, 45}
+block 0065
+  pred 0127
+  live in:{15, 17, 45, 46}
+  Blort.java:24@0065: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v45:Ljava/io/PrintStream; v46:Ljava/lang/String;="object -> strin
+  g (modified)"
+  next 0068
+  live out:{15, 17}
+block 0128
+  pred 0068
+  live in:{15, 17, 47}
+  Blort.java:25@006a: Rop{move-result-pseudo N006aLjava/lang/Object; <- . flows
+  } v48:N006aLjava/lang/Object; <- .
+  Blort.java:25@006a: goto . <- .
+  next 006d
+  live out:{15, 17, 47, 48}
+block 0068
+  pred 0065
+  live in:{15, 17}
+  Blort.java:25@0069: const-int(4) v47:I=4 <- .
+  Blort.java:25@006a: new-instance(java.lang.Object catch) . <- .
+  next 0128
+  live out:{15, 17, 47}
+block 006d
+  pred 0128
+  live in:{15, 17, 47, 48}
+  Blort.java:25@006e: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+  y>}(java.lang.Object.<init>:()V catch) . <- v48:N006aLjava/lang/Object;
+  next 0071
+  live out:{15, 17, 47, 48}
+block 0071
+  pred 006d
+  live in:{15, 17, 47, 48}
+  Blort.java:25@0071: aput-object(catch) . <- v48:Ljava/lang/Object; v17:[Ljava
+  /lang/Object; v47:I=4
+  next 0072
+  live out:{15, 17}
+block 0129
+  pred 0072
+  live in:{15, 17, 49, 50}
+  Blort.java:27@0077: Rop{move-result-pseudo I <- . flows} v51:I <- .
+  Blort.java:27@0077: goto . <- .
+  next 0078
+  live out:{15, 17, 49, 50, 51}
+block 0072
+  pred 0071
+  live in:{15, 17}
+  Blort.java:27@0073: const-int(0) v49:I=0 <- .
+  Blort.java:27@0075: const-int(0) v50:I=0 <- .
+  Blort.java:27@0077: array-length(catch java.lang.ArrayStoreException) . <- v1
+  5:[Ljava/lang/String;
+  next 012d
+  next 0129 *
+  live out:{15, 17, 49, 50}
+block 0078
+  pred 0129
+  live in:{15, 17, 49, 50, 51}
+  Blort.java:27@0078: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch java.lang.ArrayStoreException) . <- v17:[Ljava/la
+  ng/Object; v49:I=0 v15:[Ljava/lang/String; v50:I=0 v51:I
+  next 012e
+  next 007b *
+  live out:{}
+block 007b
+  pred 0078
+  live in:{}
+  Blort.java:31@007b: goto . <- .
+  next 0088
+  live out:{}
+block 012a
+  pred 007e
+  live in:{}
+  Blort.java:30@0080: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v59:Ljava/io/PrintStream; <- .
+  Blort.java:30@0080: goto . <- .
+  next 0083
+  live out:{59}
+block 007e
+  pred 0107
+  live in:{58}
+  @????: mark-local-object . <- v58:"ase"Ljava/lang/ArrayStoreException;
+  Blort.java:30@0080: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 012a
+  live out:{}
+block 012b
+  pred 0083
+  live in:{59}
+  Blort.java:30@0083: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v60
+  :Ljava/lang/String;="caught ArrayStoreException (expected)" <- .
+  Blort.java:30@0083: goto . <- .
+  next 0085
+  live out:{59, 60}
+block 0083
+  pred 012a
+  live in:{59}
+  Blort.java:30@0083: const-object("caught ArrayStoreException (expected)" catc
+  h) . <- .
+  next 012b
+  live out:{59}
+block 0085
+  pred 012b
+  live in:{59, 60}
+  Blort.java:30@0085: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v59:Ljava/io/PrintStream; v60:Ljava/lang/String;="caught ArraySto
+  reException (expected)"
+  next 012f
+  live out:{}
+block 0088
+  pred 007b
+  pred 012f
+  live in:{}
+  Blort.java:32@0088: goto . <- .
+  next 0113
+  live out:{}
+block 0113
+  pred 0088
+  live in:{}
+  Blort.java:32@0088: return-void . <- .
+  next 0132
+  live out:{}
+block 0107
+  pred 012d
+  pred 012e
+  live in:{}
+  @????: phi v58:Ljava/lang/ArrayStoreException; <- v52:Ljava/lang/ArrayStoreEx
+  ception;[b=012e] v61:Ljava/lang/ArrayStoreException;[b=012d]
+  Blort.java:28@007e: goto . <- .
+  next 007e
+  live out:{58}
+block 012c
+  live in:{}
+  @????: const-int(8) v63:I=8 <- .
+  @????: const-int(0) v62:I=0 <- .
+  @????: goto . <- .
+  next 0112
+  live out:{62, 63}
+block 012d
+  pred 0072
+  live in:{}
+  Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+  lows} v61:Ljava/lang/ArrayStoreException; <- .
+  @????: goto . <- .
+  next 0107
+  live out:{61}
+block 012e
+  pred 0078
+  live in:{}
+  Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+  lows} v52:Ljava/lang/ArrayStoreException; <- .
+  @????: goto . <- .
+  next 0107
+  live out:{52}
+block 012f
+  pred 0085
+  live in:{}
+  @????: goto . <- .
+  next 0088
+  live out:{}
+block 0130
+  pred 001b
+  live in:{15, 17, 62}
+  @????: goto . <- .
+  next 001e
+  live out:{15, 17, 62}
+block 0131
+  pred 000d
+  live in:{15, 17, 62}
+  @????: goto . <- .
+  next 001e
+  live out:{15, 17, 62}
+block 0132
+  pred 0113
+  live in:{}
+  returns
+  live out:{}
+
+method testConstructor ()V
+first 0035
+block 002c
+  pred 0035
+  live in:{}
+  Blort.java:35@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:35@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 002c
+  live in:{}
+  Blort.java:35@0000: const-object-nothrow(null) v7:<null>=null <- .
+  @????: mark-local-object . <- v7:"foo"LBlort;
+  Blort.java:35@0001: goto . <- .
+  next 0002
+  live out:{7}
+block 0033
+  pred 0002
+  live in:{7}
+  Blort.java:37@0002: Rop{move-result-pseudo N0002LBlort; <- . flows} v8:N0002L
+  Blort; <- .
+  Blort.java:37@0002: goto . <- .
+  next 0005
+  live out:{7, 8}
+block 0002
+  pred 0000
+  live in:{7}
+  Blort.java:37@0002: new-instance(Blort catch java.lang.Exception) . <- .
+  next 0036
+  next 0033 *
+  live out:{7}
+block 0005
+  pred 0033
+  live in:{7, 8}
+  Blort.java:37@0006: Rop{invoke-direct . <- LBlort; call throws <any>}(Blort.<
+  init>:()V catch java.lang.Exception) . <- v8:N0002LBlort;
+  next 0037
+  next 0009 *
+  live out:{7, 8}
+block 0009
+  pred 0005
+  live in:{8}
+  @????: mark-local-object . <- v8:"foo"LBlort;
+  Blort.java:37@0009: goto . <- .
+  next 000a
+  live out:{8}
+block 000a
+  pred 0009
+  live in:{8}
+  Blort.java:39@000a: goto . <- .
+  next 000e
+  live out:{8}
+block 000d
+  pred 0023
+  live in:{7}
+  Blort.java:38@000d: goto . <- .
+  next 000e
+  live out:{7}
+block 0034
+  pred 000e
+  live in:{14}
+  Blort.java:40@000e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v15:Ljava/io/PrintStream; <- .
+  Blort.java:40@000e: goto . <- .
+  next 0011
+  live out:{14, 15}
+block 000e
+  pred 000a
+  pred 000d
+  live in:{}
+  @????: phi v14:"foo"LBlort; <- v8:"foo"LBlort;[b=000a] v7:"foo"LBlort;[b=000d
+  ]
+  Blort.java:40@000e: get-static-object(java.lang.System.err:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0034
+  live out:{14}
+block 0011
+  pred 0034
+  live in:{14, 15}
+  Blort.java:40@0012: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  Object; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/Object;)V 
+  catch) . <- v15:Ljava/io/PrintStream; v14:LBlort;
+  next 0015
+  live out:{}
+block 0015
+  pred 0011
+  live in:{}
+  Blort.java:41@0015: goto . <- .
+  next 002d
+  live out:{}
+block 002d
+  pred 0015
+  live in:{}
+  Blort.java:41@0015: return-void . <- .
+  next 0038
+  live out:{}
+block 0023
+  pred 0036
+  pred 0037
+  live in:{7}
+  Blort.java:38@000d: goto . <- .
+  next 000d
+  live out:{7}
+block 0035
+  live in:{}
+  @????: goto . <- .
+  next 002c
+  live out:{}
+block 0036
+  pred 0002
+  live in:{7}
+  Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v19:
+  Ljava/lang/Exception; <- .
+  @????: goto . <- .
+  next 0023
+  live out:{7}
+block 0037
+  pred 0005
+  live in:{7}
+  Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v9:L
+  java/lang/Exception; <- .
+  @????: goto . <- .
+  next 0023
+  live out:{7}
+block 0038
+  pred 002d
+  live in:{}
+  returns
+  live out:{}
+
+method parseHeaderGroup (Ljava/lang/Object;Ljava/lang/Object;II)V
+first 01c6
+block 01ae
+  pred 01c6
+  live in:{99, 100, 101}
+  Blort.java:57@0000: move-param-object(0) v15:"headGroup"Ljava/lang/Object; <-
+   .
+  Blort.java:57@0000: move-param-object(1) v16:"inbuffer"Ljava/lang/Object; <- 
+  .
+  Blort.java:57@0000: move-param-int(2) v17:"maxHeaderCount"I <- .
+  Blort.java:57@0000: move-param-int(3) v18:"maxLineLen"I <- .
+  Blort.java:57@0000: goto . <- .
+  next 0000
+  live out:{16, 17, 18, 99, 100, 101}
+block 0000
+  pred 01ae
+  live in:{16, 17, 18, 99, 100, 101}
+  Blort.java:57@0000: const-object-nothrow(null) v19:<null>=null <- .
+  @????: mark-local-object . <- v19:"current"Ljava/lang/StringBuilder;
+  Blort.java:58@0003: const-object-nothrow(null) v20:<null>=null <- .
+  @????: mark-local-object . <- v20:"previous"Ljava/lang/StringBuilder;
+  Blort.java:58@0004: goto . <- .
+  next 01ca
+  live out:{16, 17, 18, 19, 20, 99, 100, 101}
+block 0006
+  pred 01ca
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:60@0008: if-nez-object . <- v31:Ljava/lang/StringBuilder;
+  next 000b *
+  next 0019
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b5
+  pred 000b
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  Blort.java:61@000b: Rop{move-result-pseudo N000bLjava/lang/StringBuilder; <- 
+  . flows} v32:N000bLjava/lang/StringBuilder; <- .
+  Blort.java:61@000b: goto . <- .
+  next 000e
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 000b
+  pred 0006
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  Blort.java:61@000b: new-instance(java.lang.StringBuilder catch) . <- .
+  next 01b5
+  live out:{16, 17, 18, 30, 99, 100, 101}
+block 000e
+  pred 01b5
+  live in:{16, 17, 18, 30, 32, 99, 100, 101}
+  Blort.java:61@000f: const-int(64) v33:I=64 <- .
+  Blort.java:61@0011: Rop{invoke-direct . <- Ljava/lang/StringBuilder; I call t
+  hrows <any>}(java.lang.StringBuilder.<init>:(I)V catch) . <- v32:N000bLjava/l
+  ang/StringBuilder; v33:I=64
+  next 0014
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 0014
+  pred 000e
+  live in:{16, 17, 18, 30, 32, 99, 100, 101}
+  @????: mark-local-object . <- v32:"current"Ljava/lang/StringBuilder;
+  Blort.java:61@0016: goto . <- .
+  next 001f
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 01b6
+  pred 0019
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:63@001b: goto . <- .
+  next 001e
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 0019
+  pred 0006
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:63@001b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v31:Ljava/lang/Str
+  ingBuilder;
+  next 01b6
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 001e
+  pred 01b6
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  @????: goto . <- .
+  next 001f
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b7
+  pred 001f
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:65@0020: Rop{move-result I <- . flows} v40:I <- .
+  Blort.java:65@0020: goto . <- .
+  next 0023
+  live out:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+block 001f
+  pred 0014
+  pred 001e
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  @????: phi v39:"current"Ljava/lang/StringBuilder; <- v32:"current"Ljava/lang/
+  StringBuilder;[b=0014] v31:"current"Ljava/lang/StringBuilder;[b=001e]
+  Blort.java:65@0020: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <a
+  ny>}(java.lang.Object.hashCode:()I catch) . <- v16:Ljava/lang/Object;
+  next 01b7
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0023
+  pred 01b7
+  live in:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+  @????: mark-local-int . <- v40:"l"I
+  Blort.java:66@0027: const-int(-1) v41:I=-1 <- .
+  Blort.java:66@0028: if-eq-int . <- v40:I v41:I=-1
+  next 002b *
+  next 01d4
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01b8
+  pred 002b
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:66@002d: Rop{move-result I <- . flows} v42:I <- .
+  Blort.java:66@002d: goto . <- .
+  next 0030
+  live out:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+block 002b
+  pred 0023
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:66@002d: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01b8
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0030
+  pred 01b8
+  live in:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+  Blort.java:66@0030: const-int(1) v43:I=1 <- .
+  Blort.java:66@0031: if-ge-int . <- v42:I v43:I=1
+  next 0034 *
+  next 0037
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0034
+  pred 0030
+  live in:{}
+  Blort.java:67@0034: goto . <- .
+  next 00d6
+  live out:{}
+block 01b9
+  pred 0037
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@003a: Rop{move-result C <- . flows} v45:C <- .
+  Blort.java:70@003a: goto . <- .
+  next 003d
+  live out:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+block 0037
+  pred 0030
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@003a: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v100:I=0
+  next 01b9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 003d
+  pred 01b9
+  live in:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+  Blort.java:70@003f: if-eq-int . <- v45:I v99:I=32
+  next 0042 *
+  next 01d3
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ba
+  pred 0042
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@0045: Rop{move-result C <- . flows} v48:C <- .
+  Blort.java:70@0045: goto . <- .
+  next 0048
+  live out:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+block 0042
+  pred 003d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@0045: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v100:I=0
+  next 01ba
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0048
+  pred 01ba
+  live in:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+  Blort.java:70@004a: if-ne-int . <- v48:I v101:I=9
+  next 01d2 *
+  next 01d1
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 004d
+  pred 01c9
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@004f: if-eqz-object . <- v30:Ljava/lang/StringBuilder;
+  next 0052 *
+  next 01d0
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0052
+  pred 004d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:71@0052: const-int(0) v67:I=0 <- .
+  @????: mark-local-int . <- v67:"i"I
+  Blort.java:71@0053: goto . <- .
+  next 0055
+  live out:{16, 17, 18, 30, 39, 67, 99, 100, 101}
+block 01bb
+  pred 0055
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:72@0059: Rop{move-result I <- . flows} v72:I <- .
+  Blort.java:72@0059: goto . <- .
+  next 005c
+  live out:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+block 0055
+  pred 0052
+  pred 0079
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: phi v71:"i"I <- v67:"i"I[b=0052] v78:"i"I[b=0079]
+  Blort.java:72@0059: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01bb
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 005c
+  pred 01bb
+  live in:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+  Blort.java:72@005c: if-ge-int . <- v71:I v72:I
+  next 005f *
+  next 01cf
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bc
+  pred 005f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:73@0063: Rop{move-result C <- . flows} v73:C <- .
+  Blort.java:73@0063: goto . <- .
+  next 0066
+  live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 005f
+  pred 005c
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:73@0063: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v71:I
+  next 01bc
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0066
+  pred 01bc
+  live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+  @????: mark-local-int . <- v73:"ch"C
+  Blort.java:74@006c: if-eq-int . <- v73:I v99:I=32
+  next 006f *
+  next 01ce
+  live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 006f
+  pred 0066
+  live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+  Blort.java:74@0073: if-eq-int . <- v73:I v101:I=9
+  next 0076 *
+  next 01cd
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0076
+  pred 006f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:75@0076: goto . <- .
+  next 01c8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0079
+  pred 01cd
+  pred 01ce
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:77@0079: add-const-int(1) v78:"i"I <- v71:I
+  Blort.java:78@007c: goto . <- .
+  next 0055
+  live out:{16, 17, 18, 30, 39, 78, 99, 100, 101}
+block 007f
+  pred 01c8
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0080: if-lez-int . <- v18:I
+  next 0083 *
+  next 01cc
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bd
+  pred 0083
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0085: Rop{move-result I <- . flows} v82:I <- .
+  Blort.java:79@0085: goto . <- .
+  next 0088
+  live out:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+block 0083
+  pred 007f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0085: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v30:Ljava/lang/Str
+  ingBuilder;
+  next 01bd
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01be
+  pred 0088
+  live in:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+  Blort.java:79@008c: Rop{move-result I <- . flows} v85:I <- .
+  Blort.java:79@008c: goto . <- .
+  next 008f
+  live out:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+block 0088
+  pred 01bd
+  live in:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+  Blort.java:79@0089: add-const-int(1) v84:I <- v82:I
+  Blort.java:79@008c: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01be
+  live out:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+block 008f
+  pred 01be
+  live in:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+  Blort.java:79@008f: add-int v86:I <- v84:I v85:I
+  Blort.java:79@0092: sub-int v87:I <- v86:I v71:I
+  Blort.java:79@0094: if-le-int . <- v87:I v18:I
+  next 0097 *
+  next 01cb
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bf
+  pred 0097
+  live in:{}
+  Blort.java:81@0097: Rop{move-result-pseudo N0097Ljava/io/IOException; <- . fl
+  ows} v88:N0097Ljava/io/IOException; <- .
+  Blort.java:81@0097: goto . <- .
+  next 009a
+  live out:{88}
+block 0097
+  pred 008f
+  live in:{}
+  Blort.java:81@0097: new-instance(java.io.IOException catch) . <- .
+  next 01bf
+  live out:{}
+block 01c0
+  pred 009a
+  live in:{88}
+  Blort.java:81@009b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v89
+  :Ljava/lang/String;="Maximum line length limit exceeded" <- .
+  Blort.java:81@009b: goto . <- .
+  next 009d
+  live out:{88, 89}
+block 009a
+  pred 01bf
+  live in:{88}
+  Blort.java:81@009b: const-object("Maximum line length limit exceeded" catch) 
+  . <- .
+  next 01c0
+  live out:{88}
+block 009d
+  pred 01c0
+  live in:{88, 89}
+  Blort.java:81@009d: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+  tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+  tch) . <- v88:N0097Ljava/io/IOException; v89:Ljava/lang/String;="Maximum line
+   length limit exceeded"
+  next 00a0
+  live out:{88}
+block 00a0
+  pred 009d
+  live in:{88}
+  Blort.java:81@00a0: throw(catch) . <- v88:Ljava/io/IOException;
+  next 01d5
+  live out:{}
+block 01c1
+  pred 00a1
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:83@00a5: goto . <- .
+  next 00a8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 00a1
+  pred 01cb
+  pred 01cc
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:83@00a5: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.append:(C)Ljava/lang/StringBuilder; cat
+  ch) . <- v30:Ljava/lang/StringBuilder; v99:I=32
+  next 01c1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c2
+  pred 00a8
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:84@00b1: Rop{move-result I <- . flows} v94:I <- .
+  Blort.java:84@00b1: goto . <- .
+  next 00b4
+  live out:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+block 00a8
+  pred 01c1
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:84@00b1: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01c2
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c3
+  pred 00b4
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:84@00b7: goto . <- .
+  next 00ba
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00b4
+  pred 01c2
+  live in:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+  Blort.java:84@00b6: sub-int v95:I <- v94:I v71:I
+  Blort.java:84@00b7: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/CharSequence; I I call throws <any>}(java.lang.StringBuilder.append:(Ljav
+  a/lang/CharSequence;II)Ljava/lang/StringBuilder; catch) . <- v30:Ljava/lang/S
+  tringBuilder; v39:Ljava/lang/StringBuilder; v71:I v95:I
+  next 01c3
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00ba
+  pred 01c3
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:85@00bb: goto . <- .
+  next 01c7
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00be
+  pred 01d0
+  pred 01d1
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  Blort.java:86@00c0: move-object v52:"previous"Ljava/lang/StringBuilder; <- v3
+  9:Ljava/lang/StringBuilder;
+  Blort.java:87@00c2: const-object-nothrow(null) v53:<null>=null <- .
+  @????: mark-local-object . <- v53:"current"Ljava/lang/StringBuilder;
+  Blort.java:87@00c3: goto . <- .
+  next 01c7
+  live out:{16, 17, 18, 52, 53, 99, 100, 101}
+block 00c5
+  pred 01c7
+  live in:{16, 17, 18, 61, 62, 99, 100, 101}
+  Blort.java:89@00c6: if-lez-int . <- v17:I
+  next 00c9 *
+  next 00d3
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c4
+  pred 00c9
+  live in:{}
+  Blort.java:90@00c9: Rop{move-result-pseudo N00c9Ljava/io/IOException; <- . fl
+  ows} v63:N00c9Ljava/io/IOException; <- .
+  Blort.java:90@00c9: goto . <- .
+  next 00cc
+  live out:{63}
+block 00c9
+  pred 00c5
+  live in:{}
+  Blort.java:90@00c9: new-instance(java.io.IOException catch) . <- .
+  next 01c4
+  live out:{}
+block 01c5
+  pred 00cc
+  live in:{63}
+  Blort.java:90@00cd: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v64
+  :Ljava/lang/String;="Maximum header count exceeded" <- .
+  Blort.java:90@00cd: goto . <- .
+  next 00cf
+  live out:{63, 64}
+block 00cc
+  pred 01c4
+  live in:{63}
+  Blort.java:90@00cd: const-object("Maximum header count exceeded" catch) . <- 
+  .
+  next 01c5
+  live out:{63}
+block 00cf
+  pred 01c5
+  live in:{63, 64}
+  Blort.java:90@00cf: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+  tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+  tch) . <- v63:N00c9Ljava/io/IOException; v64:Ljava/lang/String;="Maximum head
+  er count exceeded"
+  next 00d2
+  live out:{63}
+block 00d2
+  pred 00cf
+  live in:{63}
+  Blort.java:90@00d2: throw(catch) . <- v63:Ljava/io/IOException;
+  next 01d5
+  live out:{}
+block 00d3
+  pred 00c5
+  live in:{16, 17, 18, 61, 62, 99, 100, 101}
+  Blort.java:92@00d3: goto . <- .
+  next 01ca
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 00d6
+  pred 0034
+  pred 01d4
+  live in:{}
+  Blort.java:93@00d6: goto . <- .
+  next 01af
+  live out:{}
+block 01af
+  pred 00d6
+  live in:{}
+  Blort.java:93@00d6: return-void . <- .
+  next 01d5
+  live out:{}
+block 01c6
+  live in:{}
+  @????: const-int(9) v101:I=9 <- .
+  @????: const-int(0) v100:I=0 <- .
+  @????: const-int(32) v99:I=32 <- .
+  @????: goto . <- .
+  next 01ae
+  live out:{99, 100, 101}
+block 01c7
+  pred 00ba
+  pred 00be
+  live in:{16, 17, 18, 99, 100, 101}
+  @????: phi v61:"previous"Ljava/lang/StringBuilder; <- v52:"previous"Ljava/lan
+  g/StringBuilder;[b=00be] v30:"previous"Ljava/lang/StringBuilder;[b=00ba]
+  @????: phi v62:"current"Ljava/lang/StringBuilder; <- v53:"current"Ljava/lang/
+  StringBuilder;[b=00be] v39:"current"Ljava/lang/StringBuilder;[b=00ba]
+  @????: goto . <- .
+  next 00c5
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c8
+  pred 0076
+  pred 01cf
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 007f
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c9
+  pred 01d2
+  pred 01d3
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 004d
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ca
+  pred 0000
+  pred 00d3
+  live in:{16, 17, 18, 99, 100, 101}
+  @????: phi v30:"previous"Ljava/lang/StringBuilder; <- v20:"previous"Ljava/lan
+  g/StringBuilder;[b=0000] v61:"previous"Ljava/lang/StringBuilder;[b=00d3]
+  @????: phi v31:"current"Ljava/lang/StringBuilder; <- v19:"current"Ljava/lang/
+  StringBuilder;[b=0000] v62:"current"Ljava/lang/StringBuilder;[b=00d3]
+  @????: goto . <- .
+  next 0006
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01cb
+  pred 008f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 00a1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cc
+  pred 007f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 00a1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cd
+  pred 006f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 0079
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01ce
+  pred 0066
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 0079
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cf
+  pred 005c
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 01c8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01d0
+  pred 004d
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 00be
+  live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d1
+  pred 0048
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 00be
+  live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d2
+  pred 0048
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 01c9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d3
+  pred 003d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 01c9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d4
+  pred 0023
+  live in:{}
+  @????: goto . <- .
+  next 00d6
+  live out:{}
+block 01d5
+  pred 00a0
+  pred 00d2
+  pred 01af
+  live in:{}
+  returns
+  live out:{}
diff --git a/dx/tests/087-ssa-local-vars/info.txt b/dx/tests/087-ssa-local-vars/info.txt
new file mode 100644
index 0000000..6e9d675
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/info.txt
@@ -0,0 +1,5 @@
+This is a test case to ensure proper preservation of local variable information through the register renamer and dead code remover at the beginning of the SSA conversion.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/087-ssa-local-vars/run b/dx/tests/087-ssa-local-vars/run
new file mode 100644
index 0000000..ae97c15
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --ssa-blocks Blort.class
diff --git a/dx/tests/088-ssa-combine-blocks/Blort.java b/dx/tests/088-ssa-combine-blocks/Blort.java
new file mode 100644
index 0000000..6449883
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/Blort.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    /**
+     * just because this should do nothing
+     */
+    void voidFunction() {
+    }
+
+    /**
+     * Current SSA form requires each move-exception block to have
+     * a unique predecessor
+     */
+    void edgeSplitMoveException() {
+        try { 
+            hashCode();
+            hashCode();
+        } catch (Throwable tr) {
+        }
+    }
+
+    /**
+     * An empty infinite loop for the empty goto optimizer
+     */
+    void infiniteLoop() {
+        while (true) {
+        }
+    }
+}
+
diff --git a/dx/tests/088-ssa-combine-blocks/expected.txt b/dx/tests/088-ssa-combine-blocks/expected.txt
new file mode 100644
index 0000000..fef77e3
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/expected.txt
@@ -0,0 +1,83 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:17@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method voidFunction ()V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:23@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:23@0000: goto . <- .
+  next 0003
+block 0003
+  pred 0002
+  Blort.java:23@0000: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+  pred 0027
+  Blort.java:31@0000: move-param-object(0) v1:LBlort; <- .
+  Blort.java:31@0000: goto . <- .
+  next 0000
+block 0000
+  pred 001e
+  Blort.java:31@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+  next 0028
+  next 0004 *
+block 0004
+  pred 0000
+  Blort.java:32@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+  next 0028
+  next 001f *
+block 001f
+  pred 0004
+  pred 0028
+  Blort.java:35@000e: return-void . <- .
+  returns
+block 0027
+  @????: goto . <- .
+  next 001e
+block 0028
+  pred 0000
+  pred 0004
+  Blort.java:33@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v0:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001f
+
+method infiniteLoop ()V
+first 0003
+block 0002
+  pred 0003
+  Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:41@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0000
+  pred 0002
+  Blort.java:41@0000: goto . <- .
+  next 0000
+block 0003
+  @????: goto . <- .
+  next 0002
diff --git a/dx/tests/088-ssa-combine-blocks/info.txt b/dx/tests/088-ssa-combine-blocks/info.txt
new file mode 100644
index 0000000..603c1c0
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the identical-block combining algorithm, which runs after the SSA optimizer to recombine identical blocks (usually exception handlers) created during edge-splitting.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/088-ssa-combine-blocks/run b/dx/tests/088-ssa-combine-blocks/run
new file mode 100644
index 0000000..29fe559
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --optimize --rop-blocks Blort.class
diff --git a/dx/tests/089-dex-define-object/Class.java b/dx/tests/089-dex-define-object/Class.java
new file mode 100644
index 0000000..4de5e70
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Class.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public class Class<T> {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/Object.java b/dx/tests/089-dex-define-object/Object.java
new file mode 100644
index 0000000..e4d9e3c
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Object.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public class Object {
+    public Object() { 
+        // This space intentionally left blank.
+    }
+
+    public boolean equals(Object o) {
+        return true; 
+    }
+
+    protected void finalize() { 
+        // This space intentionally left blank.
+    }
+
+    public final native Class<? extends Object> getClass();
+    public native int hashCode();
+    public final native void notify();
+    public final native void notifyAll();
+
+    public String toString() {
+        return "blort"; 
+    }
+
+    public final void wait() {
+        wait(0, 0);
+    }
+
+    public final void wait(long time) {
+        wait(time, 0);
+    }
+
+    public final native void wait(long time, int frac);
+}
diff --git a/dx/tests/089-dex-define-object/String.java b/dx/tests/089-dex-define-object/String.java
new file mode 100644
index 0000000..26e7370
--- /dev/null
+++ b/dx/tests/089-dex-define-object/String.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public final class String {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/expected.txt b/dx/tests/089-dex-define-object/expected.txt
new file mode 100644
index 0000000..8ba83ea
--- /dev/null
+++ b/dx/tests/089-dex-define-object/expected.txt
@@ -0,0 +1,2 @@
+javac 1.7.0-internal_bootstrap
+Good!
diff --git a/dx/tests/089-dex-define-object/info.txt b/dx/tests/089-dex-define-object/info.txt
new file mode 100644
index 0000000..e035834
--- /dev/null
+++ b/dx/tests/089-dex-define-object/info.txt
@@ -0,0 +1,4 @@
+This tests that a stripped down definition of the class Object can in
+fact be converted to a dex file. This test ensures that the conversion
+runs without failure, though the contents of the converted file are
+not checked for correctness.
diff --git a/dx/tests/089-dex-define-object/run b/dx/tests/089-dex-define-object/run
new file mode 100644
index 0000000..e53e147
--- /dev/null
+++ b/dx/tests/089-dex-define-object/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --core-library --output=blort.dex */*/*.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/090-dex-unify-arrays/Blort.java b/dx/tests/090-dex-unify-arrays/Blort.java
new file mode 100644
index 0000000..47e1745
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/Blort.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort 
+{
+    /*
+     * Note: The use of the casts after the "?" in the following are
+     * to avoid a bug in some (source code level) compilers.
+     */
+
+    static public Object test1(boolean b) {
+        return (b ? new String[1] : new Integer[1])[0];
+    }
+
+    static public int test2(boolean b) {
+        Object o = b ? (Object) new int[1] : new float[1];
+        return o.hashCode();
+    }
+
+    static public int test3(boolean b) {
+        Object o = b ? (Object) new char[1] : new double[1];
+        return o.hashCode();
+    }
+
+    static public int test4(boolean b) {
+        Object o = b ? (Object) new long[1] : new boolean[1];
+        return o.hashCode();
+    }
+
+    static public int test5(boolean b) {
+        Object o = b ? (Object) new short[1] : new Object[1];
+        return o.hashCode();
+    }
+
+    static public int test6(boolean b) {
+        Object o = b ? (Object) new byte[1] : new boolean[1];
+        return o.hashCode();
+    }
+
+    static public Object test7(boolean b) {
+        return (b ? new String[1] : new int[1][])[0];
+    }
+
+    static public Object[] test8(boolean b) {
+        return (b ? new String[1][] : new int[1][][])[0];
+    }
+}
diff --git a/dx/tests/090-dex-unify-arrays/expected.txt b/dx/tests/090-dex-unify-arrays/expected.txt
new file mode 100644
index 0000000..818e985
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/expected.txt
@@ -0,0 +1,123 @@
+javac 1.7.0-internal_bootstrap
+Blort.test1:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, java.lang.Integer[]
+  000f: goto 0007 // -0008
+Blort.test2:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, int[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, float[]
+  0012: goto 0007 // -000b
+Blort.test3:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, char[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, double[]
+  0012: goto 0007 // -000b
+Blort.test4:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, long[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, boolean[]
+  0012: goto 0007 // -000b
+Blort.test5:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, short[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, java.lang.Object[]
+  0012: goto 0007 // -000b
+Blort.test6:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, byte[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, boolean[]
+  0012: goto 0007 // -000b
+Blort.test7:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, int[][]
+  000f: goto 0007 // -0008
+Blort.test8:(Z)[Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[][]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, int[][][]
+  000f: goto 0007 // -0008
diff --git a/dx/tests/090-dex-unify-arrays/info.txt b/dx/tests/090-dex-unify-arrays/info.txt
new file mode 100644
index 0000000..018fd25
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+array type unification works properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/090-dex-unify-arrays/run b/dx/tests/090-dex-unify-arrays/run
new file mode 100644
index 0000000..47b709c
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/091-ssa-const-collector/Blort.java b/dx/tests/091-ssa-const-collector/Blort.java
new file mode 100644
index 0000000..40626e2
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/Blort.java
@@ -0,0 +1,65 @@
+
+class Blort {
+    /** Class constructors for enums use a lot of const's */
+    enum Foo {
+        ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT
+    }
+    
+    /** all uses of 10 should be combined except the local assignment */
+    void testNumeric() {
+        int foo = 10;
+
+        for (int i = 0; i < 10; i++){
+            foo += i * 10;
+        }
+
+        for (int i = 0; i < 10; i++){
+            foo += i + 10;
+        }
+    }
+
+    void testStrings() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+    }
+
+    void testCaughtStrings() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        try {
+            sb.append("foo");
+            sb.append("foo");
+            sb.append("foo");
+        } catch (Throwable tr) {
+            System.out.println("foo");
+        }
+    }
+
+    /** local variables cannot be intermingled */
+    void testLocalVars() {
+        int i = 10;
+        int j = 10;
+        int k = 10;
+        int a = 10;
+        int b = 10;
+        int c = 10;
+
+        i *= 10;
+    }
+
+    void testNull(Object a) {
+        a.equals(null);
+        a.equals(null);
+
+    }
+}
+
diff --git a/dx/tests/091-ssa-const-collector/expected.txt b/dx/tests/091-ssa-const-collector/expected.txt
new file mode 100644
index 0000000..ef82e8d
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/expected.txt
@@ -0,0 +1,476 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:4@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testNumeric ()V
+first 005e
+block 005c
+  pred 005e
+  Blort.java:10@0000: move-param-object(0) v4:"this"LBlort; <- .
+  Blort.java:10@0000: goto . <- .
+  next 0000
+block 0000
+  pred 005c
+  Blort.java:10@0000: const-int(10) v0:I=10 <- .
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:12@0003: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"i"I
+  Blort.java:12@0004: goto . <- .
+  next 0005
+block 0005
+  pred 0000
+  pred 000b
+  Blort.java:12@0008: if-ge-int . <- v1:I v3:I=10
+  next 000b *
+  next 0018
+block 000b
+  pred 0005
+  Blort.java:13@000f: mul-const-int(10) v2:I <- v1:I
+  Blort.java:13@0010: add-int v0:I <- v0:I v2:I
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:12@0012: add-const-int(1) v1:"i"I <- v1:I
+  Blort.java:12@0015: goto . <- .
+  next 0005
+block 0018
+  pred 0005
+  Blort.java:16@0018: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"i"I
+  Blort.java:16@0019: goto . <- .
+  next 001a
+block 001a
+  pred 0018
+  pred 0020
+  Blort.java:16@001d: if-ge-int . <- v1:I v3:I=10
+  next 0020 *
+  next 005d
+block 0020
+  pred 001a
+  Blort.java:17@0024: add-const-int(10) v2:I <- v1:I
+  Blort.java:17@0025: add-int v0:I <- v0:I v2:I
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:16@0027: add-const-int(1) v1:"i"I <- v1:I
+  Blort.java:16@002a: goto . <- .
+  next 001a
+block 005d
+  pred 001a
+  Blort.java:19@002d: return-void . <- .
+  returns
+block 005e
+  @????: const-int(10) v3:I=10 <- .
+  @????: goto . <- .
+  next 005c
+
+method testStrings ()V
+first 0078
+block 0064
+  pred 007b
+  Blort.java:22@0000: move-param-object(0) v3:"this"LBlort; <- .
+  Blort.java:22@0000: goto . <- .
+  next 0000
+block 006b
+  pred 0000
+  Blort.java:22@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <- 
+  . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+  Blort.java:22@0000: goto . <- .
+  next 0003
+block 0000
+  pred 0064
+  Blort.java:22@0000: new-instance(java.lang.StringBuilder catch) . <- .
+  next 006b
+block 0003
+  pred 006b
+  Blort.java:22@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+  ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+  StringBuilder;
+  next 0007
+block 006c
+  pred 0007
+  Blort.java:24@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:24@0009: goto . <- .
+  next 000b
+block 0007
+  pred 0003
+  @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+  Blort.java:24@0009: const-object("foo" catch) . <- .
+  next 006c
+block 000b
+  pred 006c
+  Blort.java:24@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 000e
+block 006e
+  pred 000e
+  Blort.java:25@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:25@0010: goto . <- .
+  next 0012
+block 000e
+  pred 000b
+  Blort.java:25@0010: const-object("foo" catch) . <- .
+  next 006e
+block 0012
+  pred 006e
+  Blort.java:25@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0015
+block 0070
+  pred 0015
+  Blort.java:26@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:26@0017: goto . <- .
+  next 0019
+block 0015
+  pred 0012
+  Blort.java:26@0017: const-object("foo" catch) . <- .
+  next 0070
+block 0019
+  pred 0070
+  Blort.java:26@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 001c
+block 0072
+  pred 001c
+  Blort.java:27@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:27@001e: goto . <- .
+  next 0020
+block 001c
+  pred 0019
+  Blort.java:27@001e: const-object("foo" catch) . <- .
+  next 0072
+block 0020
+  pred 0072
+  Blort.java:27@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0023
+block 0074
+  pred 0023
+  Blort.java:28@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:28@0025: goto . <- .
+  next 0027
+block 0023
+  pred 0020
+  Blort.java:28@0025: const-object("foo" catch) . <- .
+  next 0074
+block 0027
+  pred 0074
+  Blort.java:28@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 002a
+block 0076
+  pred 002a
+  Blort.java:29@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:29@002c: goto . <- .
+  next 002e
+block 002a
+  pred 0027
+  Blort.java:29@002c: const-object("foo" catch) . <- .
+  next 0076
+block 002e
+  pred 0076
+  Blort.java:29@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0065
+block 0065
+  pred 002e
+  Blort.java:30@0032: return-void . <- .
+  returns
+block 0078
+  @????: goto . <- .
+  next 007a
+block 007a
+  pred 0078
+  @????: const-object("foo" catch) . <- .
+  next 007b
+block 007b
+  pred 007a
+  @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:Ljava/lang/St
+  ring;="foo" <- .
+  @????: goto . <- .
+  next 0064
+
+method testCaughtStrings ()V
+first 0094
+block 007e
+  pred 009e
+  Blort.java:33@0000: move-param-object(0) v5:"this"LBlort; <- .
+  Blort.java:33@0000: goto . <- .
+  next 0000
+block 0085
+  pred 0000
+  Blort.java:33@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <- 
+  . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+  Blort.java:33@0000: goto . <- .
+  next 0003
+block 0000
+  pred 007e
+  Blort.java:33@0000: new-instance(java.lang.StringBuilder catch) . <- .
+  next 0085
+block 0003
+  pred 0085
+  Blort.java:33@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+  ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+  StringBuilder;
+  next 0007
+block 0086
+  pred 0007
+  Blort.java:35@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:35@0009: goto . <- .
+  next 000b
+block 0007
+  pred 0003
+  @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+  Blort.java:35@0009: const-object("foo" catch) . <- .
+  next 0086
+block 000b
+  pred 0086
+  Blort.java:35@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 000e
+block 0088
+  pred 000e
+  Blort.java:36@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:36@0010: goto . <- .
+  next 0012
+block 000e
+  pred 000b
+  Blort.java:36@0010: const-object("foo" catch) . <- .
+  next 0088
+block 0012
+  pred 0088
+  Blort.java:36@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 0015
+block 008a
+  pred 0015
+  Blort.java:37@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:37@0017: goto . <- .
+  next 0019
+block 0015
+  pred 0012
+  Blort.java:37@0017: const-object("foo" catch) . <- .
+  next 008a
+block 0019
+  pred 008a
+  Blort.java:37@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 001d
+block 008c
+  pred 001d
+  Blort.java:39@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:39@001e: goto . <- .
+  next 0020
+block 001d
+  pred 0019
+  Blort.java:39@001e: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 008c *
+block 0020
+  pred 008c
+  Blort.java:39@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 0023 *
+block 008e
+  pred 0023
+  Blort.java:40@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:40@0025: goto . <- .
+  next 0027
+block 0023
+  pred 0020
+  Blort.java:40@0025: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 008e *
+block 0027
+  pred 008e
+  Blort.java:40@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 002a *
+block 0090
+  pred 002a
+  Blort.java:41@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:41@002c: goto . <- .
+  next 002e
+block 002a
+  pred 0027
+  Blort.java:41@002c: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 0090 *
+block 002e
+  pred 0090
+  Blort.java:41@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 007f *
+block 0092
+  pred 0035
+  Blort.java:43@0036: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v2:Ljava/io/PrintStream; <- .
+  Blort.java:43@0036: goto . <- .
+  next 0039
+block 0035
+  pred 0095
+  @????: mark-local-object . <- v1:"tr"Ljava/lang/Throwable;
+  Blort.java:43@0036: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0092
+block 0093
+  pred 0039
+  Blort.java:43@0039: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v3:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:43@0039: goto . <- .
+  next 003b
+block 0039
+  pred 0092
+  Blort.java:43@0039: const-object("foo" catch) . <- .
+  next 0093
+block 003b
+  pred 0093
+  Blort.java:43@003b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v2:Ljava/io/PrintStream; v4:Ljava/lang/String;="foo"
+  next 007f
+block 007f
+  pred 002e
+  pred 003b
+  Blort.java:45@003e: return-void . <- .
+  returns
+block 0094
+  @????: goto . <- .
+  next 009d
+block 0095
+  pred 001d
+  pred 0020
+  pred 0023
+  pred 0027
+  pred 002a
+  pred 002e
+  Blort.java:42@0035: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:L
+  java/lang/Throwable; <- .
+  @????: move-object v1:Ljava/lang/Throwable; <- v2:Ljava/lang/Throwable;
+  @????: goto . <- .
+  next 0035
+block 009d
+  pred 0094
+  @????: const-object("foo" catch) . <- .
+  next 009e
+block 009e
+  pred 009d
+  @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v4:Ljava/lang/St
+  ring;="foo" <- .
+  @????: goto . <- .
+  next 007e
+
+method testLocalVars ()V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:49@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:49@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0002
+  Blort.java:49@0000: const-int(10) v3:I=10 <- .
+  @????: mark-local-int . <- v3:"i"I
+  Blort.java:50@0003: const-int(10) v4:I=10 <- .
+  @????: mark-local-int . <- v4:"j"I
+  Blort.java:51@0006: const-int(10) v5:I=10 <- .
+  @????: mark-local-int . <- v5:"k"I
+  Blort.java:52@0009: const-int(10) v0:I=10 <- .
+  @????: mark-local-int . <- v0:"a"I
+  Blort.java:53@000d: const-int(10) v1:I=10 <- .
+  @????: mark-local-int . <- v1:"b"I
+  Blort.java:54@0011: const-int(10) v2:I=10 <- .
+  @????: mark-local-int . <- v2:"c"I
+  Blort.java:56@0018: mul-const-int(10) v3:I <- v3:I
+  @????: mark-local-int . <- v3:"i"I=100
+  Blort.java:57@001a: goto . <- .
+  next 0003
+block 0003
+  pred 0000
+  Blort.java:57@001a: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method testNull (Ljava/lang/Object;)V
+first 0021
+block 0018
+  pred 0021
+  Blort.java:60@0000: move-param-object(0) v1:"this"LBlort; <- .
+  Blort.java:60@0000: move-param-object(1) v2:"a"Ljava/lang/Object; <- .
+  Blort.java:60@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0018
+  Blort.java:60@0002: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+  ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch) 
+  . <- v2:Ljava/lang/Object; v0:<null>=null
+  next 0005
+block 0005
+  pred 0000
+  Blort.java:61@0008: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+  ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch) 
+  . <- v2:Ljava/lang/Object; v0:<null>=null
+  next 0019
+block 0019
+  pred 0005
+  Blort.java:63@000c: return-void . <- .
+  returns
+block 0021
+  @????: const-object-nothrow(null) v0:<null>=null <- .
+  @????: goto . <- .
+  next 0018
diff --git a/dx/tests/091-ssa-const-collector/info.txt b/dx/tests/091-ssa-const-collector/info.txt
new file mode 100644
index 0000000..020d3b3
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/info.txt
@@ -0,0 +1,5 @@
+This test case tests the "const collector" optimization step.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/091-ssa-const-collector/run b/dx/tests/091-ssa-const-collector/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/092-ssa-cfg-edge-cases/Blort.java b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
new file mode 100644
index 0000000..a1f264e
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
@@ -0,0 +1,21 @@
+
+class Blort {
+    
+    void testMultipleIdenticalSuccessors(int foo) {
+        switch(foo) {
+            case 1:
+            case 2:
+            case 3:
+                System.out.println("foo");
+            break;
+        }
+    }
+
+    void testNoPrimarySuccessor() {
+        try {
+            throw new RuntimeException();
+        } catch (RuntimeException ex){
+        }
+    }
+}
+
diff --git a/dx/tests/092-ssa-cfg-edge-cases/expected.txt b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
new file mode 100644
index 0000000..394550a
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
@@ -0,0 +1,121 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:2@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testMultipleIdenticalSuccessors (I)V
+first 0053
+block 004a
+  pred 0053
+  Blort.java:5@0000: move-param-object(0) v2:"this"LBlort; <- .
+  Blort.java:5@0000: move-param-int(1) v3:"foo"I <- .
+  Blort.java:5@0000: goto . <- .
+  next 0000
+block 0000
+  pred 004a
+  Blort.java:5@0001: switch({1, 2, 3}) . <- v3:I
+  next 001c
+  next 001c
+  next 001c
+  next 004b *
+block 0051
+  pred 001c
+  Blort.java:9@001c: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} v
+  0:Ljava/io/PrintStream; <- .
+  Blort.java:9@001c: goto . <- .
+  next 001f
+block 001c
+  pred 0000
+  pred 0000
+  pred 0000
+  Blort.java:9@001c: get-static-object(java.lang.System.out:Ljava/io/PrintStrea
+  m; catch) . <- .
+  next 0051
+block 0052
+  pred 001f
+  Blort.java:9@001f: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:L
+  java/lang/String;="foo" <- .
+  Blort.java:9@001f: goto . <- .
+  next 0021
+block 001f
+  pred 0051
+  Blort.java:9@001f: const-object("foo" catch) . <- .
+  next 0052
+block 0021
+  pred 0052
+  Blort.java:9@0021: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/S
+  tring; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V c
+  atch) . <- v0:Ljava/io/PrintStream; v1:Ljava/lang/String;="foo"
+  next 004b
+block 004b
+  pred 0000
+  pred 0021
+  Blort.java:12@0024: return-void . <- .
+  returns
+block 0053
+  @????: goto . <- .
+  next 004a
+
+method testNoPrimarySuccessor ()V
+first 001a
+block 0012
+  pred 001a
+  Blort.java:16@0000: move-param-object(0) v1:"this"LBlort; <- .
+  Blort.java:16@0000: goto . <- .
+  next 0000
+block 0019
+  pred 0000
+  Blort.java:16@0000: Rop{move-result-pseudo N0000Ljava/lang/RuntimeException; 
+  <- . flows} v0:N0000Ljava/lang/RuntimeException; <- .
+  Blort.java:16@0000: goto . <- .
+  next 0003
+block 0000
+  pred 0012
+  Blort.java:16@0000: new-instance(java.lang.RuntimeException catch java.lang.R
+  untimeException) . <- .
+  next 001b
+  next 0019 *
+block 0003
+  pred 0019
+  Blort.java:16@0004: Rop{invoke-direct . <- Ljava/lang/RuntimeException; call 
+  throws <any>}(java.lang.RuntimeException.<init>:()V catch java.lang.RuntimeEx
+  ception) . <- v0:N0000Ljava/lang/RuntimeException;
+  next 001b
+  next 0007 *
+block 0007
+  pred 0003
+  Blort.java:16@0007: throw(catch java.lang.RuntimeException) . <- v0:Ljava/lan
+  g/RuntimeException;
+  next 001b
+block 0013
+  pred 001b
+  Blort.java:19@0009: return-void . <- .
+  returns
+block 001a
+  @????: goto . <- .
+  next 0012
+block 001b
+  pred 0000
+  pred 0003
+  pred 0007
+  Blort.java:17@0008: Rop{move-exception Ljava/lang/RuntimeException; <- . flow
+  s} v0:Ljava/lang/RuntimeException; <- .
+  @????: goto . <- .
+  next 0013
diff --git a/dx/tests/092-ssa-cfg-edge-cases/info.txt b/dx/tests/092-ssa-cfg-edge-cases/info.txt
new file mode 100644
index 0000000..7c56302
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/info.txt
@@ -0,0 +1,5 @@
+This test case runs a few odd control flow graphs through the optimizer.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/092-ssa-cfg-edge-cases/run b/dx/tests/092-ssa-cfg-edge-cases/run
new file mode 100644
index 0000000..090a4d7
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/093-ssa-invoke-range/Blort.java b/dx/tests/093-ssa-invoke-range/Blort.java
new file mode 100644
index 0000000..a75e31f
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/Blort.java
@@ -0,0 +1,70 @@
+
+class Blort {
+    
+    static void methodThatNeedsInvokeRange
+        (int a, int b, int c, int d, int e, int f) {
+    }
+    
+    void testNoLocals() {
+        methodThatNeedsInvokeRange(5, 0, 5, 0, 5, 0);
+    }
+
+    void testMixedLocals() {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+    }
+
+    // here the current algorithm partial-overlapping will stumble a bit
+    // The register containing "zero" will be marked as "reserved for locals"
+    // Then the subsequent arraycopy will need a whole new set of 5 registers
+    void testMixedWorseCase() {
+        int src = 6;
+        int dest = 7;
+        int zero = 0;
+
+        methodThatNeedsInvokeRange(src, zero, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+    }
+
+    void testAllParams(int a, int b, int c, int d, int e, int f) {
+        methodThatNeedsInvokeRange(a, b, c, d, e, f);
+    }
+
+    // this could try to make use of param positions, but doesn't
+    static void testTailParams(int destPos, int length) {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 0, destPos, length);
+    }
+
+
+    // This presently requires a whole N new registers
+    void testFlip() {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(dest, 0, src, 1, 5, 0);
+    }
+    
+    // ensure that an attempt to combine registers for a local
+    // with a differing category doesn't mess us up.
+    long testMixedCategory(boolean foo) {
+        if (foo) {
+            int offset = 1;
+            int src = 6;
+            int dest = 7;
+
+            methodThatNeedsInvokeRange(src, 0, dest, offset, 5, 0);
+            return offset;
+        } else {
+            long offset = System.currentTimeMillis();;
+            return offset;
+        }
+    }
+}
+
diff --git a/dx/tests/093-ssa-invoke-range/expected.txt b/dx/tests/093-ssa-invoke-range/expected.txt
new file mode 100644
index 0000000..2fad8d6
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/expected.txt
@@ -0,0 +1,302 @@
+javac 1.7.0-internal_bootstrap
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:2@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method methodThatNeedsInvokeRange (IIIIII)V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:6@0000: move-param-int(0) v0:"a"I <- .
+  Blort.java:6@0000: move-param-int(1) v1:"b"I <- .
+  Blort.java:6@0000: move-param-int(2) v2:"c"I <- .
+  Blort.java:6@0000: move-param-int(3) v3:"d"I <- .
+  Blort.java:6@0000: move-param-int(4) v4:"e"I <- .
+  Blort.java:6@0000: move-param-int(5) v5:"f"I <- .
+  Blort.java:6@0000: goto . <- .
+  next 0003
+block 0003
+  pred 0002
+  Blort.java:6@0000: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method testNoLocals ()V
+first 0016
+block 0014
+  pred 0016
+  Blort.java:9@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:9@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0014
+  @????: move-int v2:I=5 <- v0:I=5
+  @????: move-int v3:I=0 <- v1:I=0
+  @????: move-int v4:I=5 <- v0:I=5
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:9@0006: Rop{invoke-static . <- I I I I I I call throws <any>}(Blor
+  t.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I=5 v1:I=0 v2:I=5 v3:I=
+  0 v4:I=5 v5:I=0
+  next 0015
+block 0015
+  pred 0000
+  Blort.java:10@0009: return-void . <- .
+  returns
+block 0016
+  @????: const-int(5) v0:I=5 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0014
+
+method testMixedLocals ()V
+first 0034
+block 0032
+  pred 0034
+  Blort.java:13@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:13@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0032
+  Blort.java:13@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:14@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:16@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 000f
+block 000f
+  pred 0000
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:17@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 0033
+block 0033
+  pred 000f
+  Blort.java:18@0018: return-void . <- .
+  returns
+block 0034
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0032
+
+method testMixedWorseCase ()V
+first 0038
+block 0036
+  pred 0038
+  Blort.java:24@0000: move-param-object(0) v12:"this"LBlort; <- .
+  Blort.java:24@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0036
+  Blort.java:24@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:25@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  Blort.java:26@0006: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"zero"I
+  Blort.java:28@000e: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I v2:I v3:I=1 v4:
+  I=5 v5:I=0
+  next 0011
+block 0011
+  pred 0000
+  @????: move-int v6:I <- v0:I
+  @????: move-int v7:I=0 <- v5:I=0
+  @????: move-int v8:I <- v2:I
+  @????: move-int v9:I=1 <- v3:I=1
+  @????: move-int v10:I=5 <- v4:I=5
+  @????: move-int v11:I=0 <- v5:I=0
+  Blort.java:29@0017: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v6:I v7:I=0 v8:I v9:I=1 v
+  10:I=5 v11:I=0
+  next 0037
+block 0037
+  pred 0011
+  Blort.java:30@001a: return-void . <- .
+  returns
+block 0038
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v5:I=0 <- .
+  @????: goto . <- .
+  next 0036
+
+method testAllParams (IIIIII)V
+first 001c
+block 001a
+  pred 001c
+  Blort.java:33@0000: move-param-object(0) v0:"this"LBlort; <- .
+  Blort.java:33@0000: move-param-int(1) v1:"a"I <- .
+  Blort.java:33@0000: move-param-int(2) v2:"b"I <- .
+  Blort.java:33@0000: move-param-int(3) v3:"c"I <- .
+  Blort.java:33@0000: move-param-int(4) v4:"d"I <- .
+  Blort.java:33@0000: move-param-int(5) v5:"e"I <- .
+  Blort.java:33@0000: move-param-int(6) v6:"f"I <- .
+  Blort.java:33@0000: goto . <- .
+  next 0000
+block 0000
+  pred 001a
+  Blort.java:33@0009: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v1:I v2:I v3:I v4:I v5:I 
+  v6:I
+  next 001b
+block 001b
+  pred 0000
+  Blort.java:34@000c: return-void . <- .
+  returns
+block 001c
+  @????: goto . <- .
+  next 001a
+
+method testTailParams (II)V
+first 0022
+block 0020
+  pred 0022
+  Blort.java:38@0000: move-param-int(0) v6:"destPos"I <- .
+  Blort.java:38@0000: move-param-int(1) v7:"length"I <- .
+  Blort.java:38@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0020
+  Blort.java:38@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:39@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v3:I=0 <- v1:I=0
+  @????: move-int v4:I <- v6:I
+  @????: move-int v5:I <- v7:I
+  Blort.java:41@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=0 v
+  4:I v5:I
+  next 0021
+block 0021
+  pred 0000
+  Blort.java:42@000f: return-void . <- .
+  returns
+block 0022
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0020
+
+method testFlip ()V
+first 0034
+block 0032
+  pred 0034
+  Blort.java:47@0000: move-param-object(0) v11:"this"LBlort; <- .
+  Blort.java:47@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0032
+  Blort.java:47@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:48@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:50@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 000f
+block 000f
+  pred 0000
+  @????: move-int v5:I <- v2:I
+  @????: move-int v6:I=0 <- v1:I=0
+  @????: move-int v7:I <- v0:I
+  @????: move-int v8:I=1 <- v3:I=1
+  @????: move-int v9:I=5 <- v4:I=5
+  @????: move-int v10:I=0 <- v1:I=0
+  Blort.java:51@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v5:I v6:I=0 v7:I v8:I=1 v
+  9:I=5 v10:I=0
+  next 0033
+block 0033
+  pred 000f
+  Blort.java:52@0018: return-void . <- .
+  returns
+block 0034
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0032
+
+method testMixedCategory (Z)J
+first 0044
+block 003c
+  pred 0044
+  Blort.java:57@0000: move-param-object(0) v8:"this"LBlort; <- .
+  Blort.java:57@0000: move-param-int(1) v9:"foo"Z <- .
+  Blort.java:57@0000: goto . <- .
+  next 0000
+block 0000
+  pred 003c
+  Blort.java:57@0001: if-eqz-int . <- v9:I
+  next 0004 *
+  next 001a
+block 0004
+  pred 0000
+  Blort.java:58@0004: const-int(1) v3:I=1 <- .
+  @????: mark-local-int . <- v3:"offset"I
+  Blort.java:59@0006: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:60@0009: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  Blort.java:62@0012: const-int(5) v4:I=5 <- .
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:62@0014: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I v4:
+  I=5 v5:I=0
+  next 0017
+block 0017
+  pred 0004
+  Blort.java:63@0018: conv-i2l v4:J <- v3:I
+  Blort.java:63@0019: goto . <- .
+  next 003d
+block 0043
+  pred 001a
+  Blort.java:65@001a: Rop{move-result J <- . flows} v6:J <- .
+  Blort.java:65@001a: goto . <- .
+  next 001d
+block 001a
+  pred 0000
+  Blort.java:65@001a: Rop{invoke-static . <- . call throws <any>}(java.lang.Sys
+  tem.currentTimeMillis:()J catch) . <- .
+  next 0043
+block 001d
+  pred 0043
+  @????: mark-local-long . <- v6:"offset"J
+  @????: move-long v4:J <- v6:"offset"J
+  Blort.java:66@001f: goto . <- .
+  next 003d
+block 003d
+  pred 0017
+  pred 001d
+  Blort.java:66@001f: return-long . <- v4:J
+  returns
+block 0044
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 003c
diff --git a/dx/tests/093-ssa-invoke-range/info.txt b/dx/tests/093-ssa-invoke-range/info.txt
new file mode 100644
index 0000000..372bed7
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/info.txt
@@ -0,0 +1,6 @@
+This test case checks the ability of the register allocator to plan
+for dex's invoke-range instruction.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/093-ssa-invoke-range/run b/dx/tests/093-ssa-invoke-range/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/094-scala-locals/blort.j b/dx/tests/094-scala-locals/blort.j
new file mode 100644
index 0000000..0d3cae5
--- /dev/null
+++ b/dx/tests/094-scala-locals/blort.j
@@ -0,0 +1,45 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort
+.super java/lang/Object
+
+.method public static returnint()I
+    .limit stack 1
+    ldc 10
+    ireturn
+.end method
+
+.method public static scalalocals()V
+    .limit locals 5
+    .limit stack 5
+    .var 4 is x I from start to end
+start:
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    dup
+    istore 4
+    istore 2
+    istore 3
+    istore 1
+    istore 0
+    iload_2
+    istore 4
+    iload_3
+end:
+    return
+.end method
+
diff --git a/dx/tests/094-scala-locals/expected.txt b/dx/tests/094-scala-locals/expected.txt
new file mode 100644
index 0000000..c74db70
--- /dev/null
+++ b/dx/tests/094-scala-locals/expected.txt
@@ -0,0 +1,85 @@
+reading Blort.class...
+method scalalocals ()V
+first 0025
+block 001a
+  pred 0025
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0021
+  pred 0000
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0000
+  pred 001a
+  live in:{}
+  blort.j:@0000: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0021
+  live out:{}
+block 0022
+  pred 0003
+  live in:{}
+  blort.j:@0003: goto . <- .
+  next 0006
+  live out:{}
+block 0003
+  pred 0021
+  live in:{}
+  blort.j:@0003: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0022
+  live out:{}
+block 0023
+  pred 0006
+  live in:{}
+  blort.j:@0006: goto . <- .
+  next 0009
+  live out:{}
+block 0006
+  pred 0022
+  live in:{}
+  blort.j:@0006: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0023
+  live out:{}
+block 0024
+  pred 0009
+  live in:{}
+  blort.j:@0009: Rop{move-result I <- . flows} v14:I <- .
+  blort.j:@0009: goto . <- .
+  next 000c
+  live out:{14}
+block 0009
+  pred 0023
+  live in:{}
+  blort.j:@0009: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0024
+  live out:{}
+block 000c
+  pred 0024
+  live in:{14}
+  @????: mark-local-int . <- v14:"x"I
+  blort.j:@001b: goto . <- .
+  next 001b
+  live out:{}
+block 001b
+  pred 000c
+  live in:{}
+  blort.j:@001b: return-void . <- .
+  next 0026
+  live out:{}
+block 0025
+  live in:{}
+  @????: goto . <- .
+  next 001a
+  live out:{}
+block 0026
+  pred 001b
+  live in:{}
+  returns
+  live out:{}
diff --git a/dx/tests/094-scala-locals/info.txt b/dx/tests/094-scala-locals/info.txt
new file mode 100644
index 0000000..cb1a42e
--- /dev/null
+++ b/dx/tests/094-scala-locals/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of the SSA renamer's local variable preserver.
+It tests a case observed from Scala, wherein a local variable is assigned
+an identical value twice. The correct result should be only a single
+mark-local, with the second assignment eaten by copy-propogation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/094-scala-locals/run b/dx/tests/094-scala-locals/run
new file mode 100644
index 0000000..4bbfa79
--- /dev/null
+++ b/dx/tests/094-scala-locals/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dump --ssa-blocks  --method=scalalocals Blort.class
diff --git a/dx/tests/095-dex-const-string-jumbo/Blort.java b/dx/tests/095-dex-const-string-jumbo/Blort.java
new file mode 100644
index 0000000..a0271b5
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/Blort.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+    static public void consume(String s) {
+        // This space intentionally left blank.
+    }
+
+    public void test() {
+        consume("zorch");
+    }
+}
diff --git a/dx/tests/095-dex-const-string-jumbo/expected.txt b/dx/tests/095-dex-const-string-jumbo/expected.txt
new file mode 100644
index 0000000..1dbcc35
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/expected.txt
@@ -0,0 +1,7 @@
+javac 1.7.0-internal_bootstrap
+Blort.test:()V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: const-string/jumbo v1, "zorch"
+  0004: invoke-static {v1}, Blort.consume:(Ljava/lang/String;)V
+  0007: return-void
diff --git a/dx/tests/095-dex-const-string-jumbo/info.txt b/dx/tests/095-dex-const-string-jumbo/info.txt
new file mode 100644
index 0000000..c14fd8e
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+const-string/jumbo gets emitted appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/095-dex-const-string-jumbo/run b/dx/tests/095-dex-const-string-jumbo/run
new file mode 100644
index 0000000..d984333
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/run
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+    writeFile("Zorch1", 0, 16383);
+    writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+    fileName = name ".java";
+    printf("public class %s {\n", name) > fileName;
+    for (i = start; i <= end; i++) {
+        printf("    static public final String s%d = \"%d\";\n",
+            i, i) > fileName;
+    }
+    printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
+
diff --git a/dx/tests/096-dex-giant-catch/Blort.java b/dx/tests/096-dex-giant-catch/Blort.java
new file mode 100644
index 0000000..f5f6e8d
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+    static public void blort(long v1, long v2, long v3, long v4,
+            long v5, long v6, long v7, long v8) {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/096-dex-giant-catch/expected.txt b/dx/tests/096-dex-giant-catch/expected.txt
new file mode 100644
index 0000000..5c1b6d4
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/expected.txt
@@ -0,0 +1,6 @@
+javac 1.7.0-internal_bootstrap
+  catches
+      try 0024..00010017
+      catch java.lang.RuntimeException -> 00011260
+      try 0001003b..0001125f
+      catch java.lang.RuntimeException -> 00011260
diff --git a/dx/tests/096-dex-giant-catch/info.txt b/dx/tests/096-dex-giant-catch/info.txt
new file mode 100644
index 0000000..b81ce94
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+very long catch ranges (that cover >= 65536 code units) get emitted
+appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/096-dex-giant-catch/run b/dx/tests/096-dex-giant-catch/run
new file mode 100644
index 0000000..c81d04c
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/run
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Write out a file with a really huge catch range.
+
+awk '
+BEGIN {
+    fileName = "Zorch.java";
+    printf("public class Zorch {\n") > fileName;
+    printf("    static public void test() {\n") > fileName;
+    printf("        try {\n") > fileName;
+    for (i = 0; i <= 1800; i++) {
+        d = i + 1000000;
+        printf("    Blort.blort(100%dL, 200%dL, 300%dL, 400%dL, 500%dL, " \
+            "600%dL, 700%dL, 800%dL);\n",
+            d, d + 1, d + 2, d + 3, d + 4, d + 5, d + 6, d + 7) > fileName;
+    }
+    printf("        } catch (RuntimeException ex) {\n") > fileName;
+    printf("            throw ex;\n") > fileName;
+    printf("        }\n") > fileName;
+    printf("    }\n") > fileName;
+    printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Zorch.test Zorch.class | grep 'try\|catch'
diff --git a/dx/tests/097-dex-branch-offset-zero/Blort.java b/dx/tests/097-dex-branch-offset-zero/Blort.java
new file mode 100644
index 0000000..5033c8f
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/Blort.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+    public void test1() {
+        for (;;) /*empty*/ ;
+    }
+
+    public void test2(int x) {
+        while (x > 0) /*empty*/ ;
+    }
+
+    public void test3(int x, int y) {
+        while (x < y) /*empty*/ ;
+    }
+}
diff --git a/dx/tests/097-dex-branch-offset-zero/expected.txt b/dx/tests/097-dex-branch-offset-zero/expected.txt
new file mode 100644
index 0000000..a57ea88
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/expected.txt
@@ -0,0 +1,2 @@
+javac 1.7.0-internal_bootstrap
+No bad branches found.
diff --git a/dx/tests/097-dex-branch-offset-zero/info.txt b/dx/tests/097-dex-branch-offset-zero/info.txt
new file mode 100644
index 0000000..4bf9502
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the only non-switch branches to offset 0 happen using the goto/32 opcode.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/097-dex-branch-offset-zero/run b/dx/tests/097-dex-branch-offset-zero/run
new file mode 100644
index 0000000..34539eb
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class | grep '[-+][0-9]' | grep -v 'goto/32.*+00*$' | grep '// +00*$'
+
+if [ "$?" = "1" ]; then
+    echo "No bad branches found."
+else
+    # Redo the dx command without filters, to aid with debugging.
+    dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class
+fi
diff --git a/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
new file mode 100644
index 0000000..ff992ca
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
Binary files differ
diff --git a/dx/tests/098-dex-jsr-ret-throw/expected.txt b/dx/tests/098-dex-jsr-ret-throw/expected.txt
new file mode 100644
index 0000000..b5b1b93
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/expected.txt
@@ -0,0 +1,652 @@
+reading ViewDebug$ViewServer.class...
+method run ()V
+first 0162
+block 0162
+  ViewDebug.java:564@0000: move-param-object(0) v0:"this"Landroid/view/ViewDebu
+  g$ViewServer; <- .
+  ViewDebug.java:564@0000: goto . <- .
+  next 0000
+block 0169
+  pred 0000
+  ViewDebug.java:564@0001: Rop{move-result-pseudo Ljava/net/ServerSocket; <- . 
+  flows} v11:Ljava/net/ServerSocket; <- .
+  ViewDebug.java:564@0001: goto . <- .
+  next 0004
+block 0000
+  pred 0162
+  ViewDebug.java:564@0000: move-object v11:Landroid/view/ViewDebug$ViewServer; 
+  <- v0:Landroid/view/ViewDebug$ViewServer;
+  ViewDebug.java:564@0001: get-field-object(android.view.ViewDebug$ViewServer.m
+  ViewServerSocket:Ljava/net/ServerSocket; catch) . <- v11:Landroid/view/ViewDe
+  bug$ViewServer;
+  next 0169
+block 0004
+  pred 0169
+  ViewDebug.java:564@0004: move-object v1:"server"Ljava/net/ServerSocket; <- v1
+  1:Ljava/net/ServerSocket;
+  ViewDebug.java:564@0004: goto . <- .
+  next 0005
+block 016a
+  pred 0005
+  ViewDebug.java:566@0005: Rop{move-result Ljava/lang/Thread; <- . flows} v11:L
+  java/lang/Thread; <- .
+  ViewDebug.java:566@0005: goto . <- .
+  next 0008
+block 0005
+  pred 0004
+  pred 00ad
+  ViewDebug.java:566@0005: Rop{invoke-static . <- . call throws <any>}(java.lan
+  g.Thread.currentThread:()Ljava/lang/Thread; catch) . <- .
+  next 016a
+block 016b
+  pred 0008
+  ViewDebug.java:566@0009: Rop{move-result-pseudo Ljava/lang/Thread; <- . flows
+  } v12:Ljava/lang/Thread; <- .
+  ViewDebug.java:566@0009: goto . <- .
+  next 000c
+block 0008
+  pred 016a
+  ViewDebug.java:566@0008: move-object v12:Landroid/view/ViewDebug$ViewServer; 
+  <- v0:Landroid/view/ViewDebug$ViewServer;
+  ViewDebug.java:566@0009: get-field-object(android.view.ViewDebug$ViewServer.m
+  Thread:Ljava/lang/Thread; catch) . <- v12:Landroid/view/ViewDebug$ViewServer;
+  next 016b
+block 000c
+  pred 016b
+  ViewDebug.java:566@000c: if-ne-object . <- v11:Ljava/lang/Thread; v12:Ljava/l
+  ang/Thread;
+  next 000f *
+  next 00b0
+block 000f
+  pred 000c
+  ViewDebug.java:567@000f: const-object-nothrow(null) v11:<null>=null <- .
+  ViewDebug.java:567@0010: move-object v2:"client"Ljava/net/Socket; <- v11:<nul
+  l>=null
+  ViewDebug.java:567@0010: goto . <- .
+  next 0011
+block 016c
+  pred 0011
+  ViewDebug.java:569@0012: Rop{move-result Ljava/net/Socket; <- . flows} v11:Lj
+  ava/net/Socket; <- .
+  ViewDebug.java:569@0012: goto . <- .
+  next 0015
+block 0011
+  pred 000f
+  ViewDebug.java:569@0011: move-object v11:Ljava/net/ServerSocket; <- v1:Ljava/
+  net/ServerSocket;
+  ViewDebug.java:569@0012: Rop{invoke-virtual . <- Ljava/net/ServerSocket; call
+   throws <any>}(java.net.ServerSocket.accept:()Ljava/net/Socket; catch java.io
+  .IOException java.lang.Object) . <- v11:Ljava/net/ServerSocket;
+  next 0130
+  next 0140
+  next 016c *
+block 0015
+  pred 016c
+  ViewDebug.java:569@0015: move-object v2:"client"Ljava/net/Socket; <- v11:Ljav
+  a/net/Socket;
+  ViewDebug.java:571@0016: const-object-nothrow(null) v11:<null>=null <- .
+  ViewDebug.java:571@0017: move-object v3:"in"Ljava/io/BufferedReader; <- v11:<
+  null>=null
+  ViewDebug.java:571@0017: goto . <- .
+  next 0018
+block 016d
+  pred 0018
+  ViewDebug.java:573@0018: Rop{move-result-pseudo N0018Ljava/io/BufferedReader;
+   <- . flows} v11:N0018Ljava/io/BufferedReader; <- .
+  ViewDebug.java:573@0018: goto . <- .
+  next 001b
+block 0018
+  pred 0015
+  ViewDebug.java:573@0018: new-instance(java.io.BufferedReader catch java.lang.
+  Object) . <- .
+  next 0116
+  next 016d *
+block 016e
+  pred 001b
+  ViewDebug.java:573@001c: Rop{move-result-pseudo N001cLjava/io/InputStreamRead
+  er; <- . flows} v13:N001cLjava/io/InputStreamReader; <- .
+  ViewDebug.java:573@001c: goto . <- .
+  next 001f
+block 001b
+  pred 016d
+  ViewDebug.java:573@001b: move-object v16:N0018Ljava/io/BufferedReader; <- v11
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001b: move-object v11:N0018Ljava/io/BufferedReader; <- v16
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001b: move-object v12:N0018Ljava/io/BufferedReader; <- v16
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001c: new-instance(java.io.InputStreamReader catch java.la
+  ng.Object) . <- .
+  next 0116
+  next 016e *
+block 016f
+  pred 001f
+  ViewDebug.java:573@0021: Rop{move-result Ljava/io/InputStream; <- . flows} v1
+  5:Ljava/io/InputStream; <- .
+  ViewDebug.java:573@0021: goto . <- .
+  next 0024
+block 001f
+  pred 016e
+  ViewDebug.java:573@001f: move-object v16:N001cLjava/io/InputStreamReader; <- 
+  v13:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@001f: move-object v13:N001cLjava/io/InputStreamReader; <- 
+  v16:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@001f: move-object v14:N001cLjava/io/InputStreamReader; <- 
+  v16:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@0020: move-object v15:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:573@0021: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.getInputStream:()Ljava/io/InputStream; catch java.la
+  ng.Object) . <- v15:Ljava/net/Socket;
+  next 0116
+  next 016f *
+block 0024
+  pred 016f
+  ViewDebug.java:573@0024: Rop{invoke-direct . <- Ljava/io/InputStreamReader; L
+  java/io/InputStream; call throws <any>}(java.io.InputStreamReader.<init>:(Lja
+  va/io/InputStream;)V catch java.lang.Object) . <- v14:N001cLjava/io/InputStre
+  amReader; v15:Ljava/io/InputStream;
+  next 0116
+  next 0027 *
+block 0027
+  pred 0024
+  ViewDebug.java:573@0027: Rop{invoke-direct . <- Ljava/io/BufferedReader; Ljav
+  a/io/Reader; call throws <any>}(java.io.BufferedReader.<init>:(Ljava/io/Reade
+  r;)V catch java.lang.Object) . <- v12:N0018Ljava/io/BufferedReader; v13:Ljava
+  /io/InputStreamReader;
+  next 0116
+  next 002a *
+block 0170
+  pred 002a
+  ViewDebug.java:574@002c: Rop{move-result Ljava/lang/String; <- . flows} v11:L
+  java/lang/String; <- .
+  ViewDebug.java:574@002c: goto . <- .
+  next 002f
+block 002a
+  pred 0027
+  ViewDebug.java:573@002a: move-object v3:"in"Ljava/io/BufferedReader; <- v11:L
+  java/io/BufferedReader;
+  ViewDebug.java:574@002b: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:574@002c: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.readLine:()Ljava/lang/String; catch ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0116
+  next 0170 *
+block 0171
+  pred 002f
+  ViewDebug.java:576@0031: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="DUMP" <- .
+  ViewDebug.java:576@0031: goto . <- .
+  next 0033
+block 002f
+  pred 0170
+  ViewDebug.java:574@002f: move-object v4:"command"Ljava/lang/String; <- v11:Lj
+  ava/lang/String;
+  ViewDebug.java:576@0031: const-object("DUMP" catch java.lang.Object) . <- .
+  next 0116
+  next 0171 *
+block 0172
+  pred 0033
+  ViewDebug.java:576@0035: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:576@0035: goto . <- .
+  next 0038
+block 0033
+  pred 0171
+  ViewDebug.java:576@0033: move-object v12:Ljava/lang/String; <- v4:Ljava/lang/
+  String;
+  ViewDebug.java:576@0035: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+  ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="DUMP" v12:Ljava/
+  lang/String;
+  next 0116
+  next 0172 *
+block 0038
+  pred 0172
+  ViewDebug.java:576@0038: if-eqz-int . <- v11:I
+  next 003b *
+  next 0042
+block 003b
+  pred 0038
+  ViewDebug.java:577@003b: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:577@003c: Rop{invoke-static . <- Ljava/net/Socket; call throws
+   <any>}(android.view.ViewDebug$ViewServer.dump:(Ljava/net/Socket;)V catch jav
+  a.lang.Object) . <- v11:Ljava/net/Socket;
+  next 0116
+  next 003f *
+block 003f
+  pred 003b
+  ViewDebug.java:577@003f: goto . <- .
+  next 005f
+block 0173
+  pred 0042
+  ViewDebug.java:579@0044: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String;=" " <- .
+  ViewDebug.java:579@0044: goto . <- .
+  next 0046
+block 0042
+  pred 0038
+  ViewDebug.java:579@0042: move-object v11:Ljava/lang/String; <- v4:Ljava/lang/
+  String;
+  ViewDebug.java:579@0044: const-object(" " catch java.lang.Object) . <- .
+  next 0116
+  next 0173 *
+block 0174
+  pred 0046
+  ViewDebug.java:579@0046: Rop{move-result [Ljava/lang/String; <- . flows} v11:
+  [Ljava/lang/String; <- .
+  ViewDebug.java:579@0046: goto . <- .
+  next 0049
+block 0046
+  pred 0173
+  ViewDebug.java:579@0046: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.split:(Ljava/lang/String;)[Ljav
+  a/lang/String; catch java.lang.Object) . <- v11:Ljava/lang/String; v12:Ljava/
+  lang/String;=" "
+  next 0116
+  next 0174 *
+block 0175
+  pred 0049
+  ViewDebug.java:580@004b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="CAPTURE" <- .
+  ViewDebug.java:580@004b: goto . <- .
+  next 004d
+block 0049
+  pred 0174
+  ViewDebug.java:579@0049: move-object v5:"params"[Ljava/lang/String; <- v11:[L
+  java/lang/String;
+  ViewDebug.java:580@004b: const-object("CAPTURE" catch java.lang.Object) . <- 
+  .
+  next 0116
+  next 0175 *
+block 0176
+  pred 004d
+  ViewDebug.java:580@0050: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String; <- .
+  ViewDebug.java:580@0050: goto . <- .
+  next 0051
+block 004d
+  pred 0175
+  ViewDebug.java:580@004d: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+  g/String;
+  ViewDebug.java:580@004f: const-int(0) v13:I=0 <- .
+  ViewDebug.java:580@0050: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+  lang/String; v13:I=0
+  next 0116
+  next 0176 *
+block 0177
+  pred 0051
+  ViewDebug.java:580@0051: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:580@0051: goto . <- .
+  next 0054
+block 0051
+  pred 0176
+  ViewDebug.java:580@0051: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+  ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="CAPTURE" v12:Lja
+  va/lang/String;
+  next 0116
+  next 0177 *
+block 0054
+  pred 0177
+  ViewDebug.java:580@0054: if-eqz-int . <- v11:I
+  next 0057 *
+  next 005f
+block 0178
+  pred 0057
+  ViewDebug.java:581@005b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String; <- .
+  ViewDebug.java:581@005b: goto . <- .
+  next 005c
+block 0057
+  pred 0054
+  ViewDebug.java:581@0057: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:581@0058: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+  g/String;
+  ViewDebug.java:581@005a: const-int(1) v13:I=1 <- .
+  ViewDebug.java:581@005b: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+  lang/String; v13:I=1
+  next 0116
+  next 0178 *
+block 005c
+  pred 0178
+  ViewDebug.java:581@005c: Rop{invoke-static . <- Ljava/net/Socket; Ljava/lang/
+  String; call throws <any>}(android.view.ViewDebug$ViewServer.capture:(Ljava/n
+  et/Socket;Ljava/lang/String;)V catch java.lang.Object) . <- v11:Ljava/net/Soc
+  ket; v12:Ljava/lang/String;
+  next 0116
+  next 005f *
+block 0065
+  pred 0116
+  ViewDebug.java:586@0065: move-object v6:Ljava/lang/Class;=java.lang.Object <-
+   v11:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:586@0065: goto . <- .
+  next 0067
+block 0062
+  pred 018d
+  ViewDebug.java:589@0062: goto . <- .
+  next 0079
+block 006a
+  pred 018a
+  ViewDebug.java:586@006a: move-object v11:Ljava/lang/Class;=java.lang.Object <
+  - v6:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:586@006c: throw(catch java.io.IOException java.lang.Object) . 
+  <- v11:Ljava/lang/Class;=java.lang.Object
+  next 0130
+  next 0140
+block 0179
+  pred 007f
+  ViewDebug.java:591@0080: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="ViewServer" <- .
+  ViewDebug.java:591@0080: goto . <- .
+  next 0082
+block 007f
+  pred 0130
+  ViewDebug.java:590@007f: move-object v3:"e"Ljava/io/IOException; <- v11:Ljava
+  /lang/Class;=java.io.IOException
+  ViewDebug.java:591@0080: const-object("ViewServer" catch java.lang.Object) . 
+  <- .
+  next 0140
+  next 0179 *
+block 017a
+  pred 0082
+  ViewDebug.java:591@0082: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String;="Connection error: " <- .
+  ViewDebug.java:591@0082: goto . <- .
+  next 0084
+block 0082
+  pred 0179
+  ViewDebug.java:591@0082: const-object("Connection error: " catch java.lang.Ob
+  ject) . <- .
+  next 0140
+  next 017a *
+block 017b
+  pred 0084
+  ViewDebug.java:591@0085: Rop{move-result I <- . flows} v11:I <- .
+  ViewDebug.java:591@0085: goto . <- .
+  next 0088
+block 0084
+  pred 017a
+  ViewDebug.java:591@0084: move-object v13:Ljava/io/IOException; <- v3:Ljava/io
+  /IOException;
+  ViewDebug.java:591@0085: Rop{invoke-static . <- Ljava/lang/String; Ljava/lang
+  /String; Ljava/lang/Throwable; call throws <any>}(android.util.Log.w:(Ljava/l
+  ang/String;Ljava/lang/String;Ljava/lang/Throwable;)I catch java.lang.Object) 
+  . <- v11:Ljava/lang/String;="ViewServer" v12:Ljava/lang/String;="Connection e
+  rror: " v13:Ljava/io/IOException;
+  next 0140
+  next 017b *
+block 0088
+  pred 017b
+  @????: goto . <- .
+  next 0089
+block 008f
+  pred 0140
+  ViewDebug.java:593@008f: move-object v8:Ljava/lang/Class;=java.lang.Object <-
+   v11:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:593@008f: goto . <- .
+  next 0091
+block 007c
+  pred 0190
+  ViewDebug.java:600@007c: goto . <- .
+  next 00ad
+block 008c
+  pred 0184
+  ViewDebug.java:600@008c: goto . <- .
+  next 00ad
+block 0094
+  pred 017e
+  ViewDebug.java:593@0094: move-object v11:Ljava/lang/Class;=java.lang.Object <
+  - v8:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:593@0096: throw(catch) . <- v11:Ljava/lang/Class;=java.lang.Ob
+  ject
+  returns
+block 00ad
+  pred 007c
+  pred 008c
+  ViewDebug.java:601@00ad: goto . <- .
+  next 0005
+block 00b0
+  pred 000c
+  ViewDebug.java:602@00b0: goto . <- .
+  next 0163
+block 0163
+  pred 00b0
+  ViewDebug.java:602@00b0: return-void . <- .
+  returns
+block 0116
+  pred 0018
+  pred 001b
+  pred 001f
+  pred 0024
+  pred 0027
+  pred 002a
+  pred 002f
+  pred 0033
+  pred 003b
+  pred 0042
+  pred 0046
+  pred 0049
+  pred 004d
+  pred 0051
+  pred 0057
+  pred 005c
+  ViewDebug.java:586@0065: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+  1:Ljava/lang/Object; <- .
+  ViewDebug.java:586@0065: goto . <- .
+  next 0065
+block 0130
+  pred 0011
+  pred 006a
+  pred 0189
+  pred 018c
+  ViewDebug.java:590@007f: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:590@007f: goto . <- .
+  next 007f
+block 0140
+  pred 0011
+  pred 006a
+  pred 007f
+  pred 0082
+  pred 0084
+  pred 0189
+  pred 018c
+  ViewDebug.java:593@008f: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+  1:Ljava/lang/Object; <- .
+  ViewDebug.java:593@008f: goto . <- .
+  next 008f
+block 017c
+  pred 0091
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 017d *
+  next 017e
+block 017d
+  pred 017c
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 017f
+  next 0180 *
+block 0180
+  pred 017d
+  ViewDebug.java:598@00a1: goto . <- .
+  next 017e
+block 017e
+  pred 017c
+  pred 0180
+  pred 0181
+  @????: goto . <- .
+  next 0094
+block 017f
+  pred 017d
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0181
+block 0181
+  pred 017f
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 017e
+block 0091
+  pred 008f
+  @????: goto . <- .
+  next 017c
+block 0182
+  pred 0089
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 0183 *
+  next 0184
+block 0183
+  pred 0182
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 0185
+  next 0186 *
+block 0186
+  pred 0183
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0184
+block 0184
+  pred 0182
+  pred 0186
+  pred 0187
+  @????: goto . <- .
+  next 008c
+block 0185
+  pred 0183
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0187
+block 0187
+  pred 0185
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 0184
+block 0089
+  pred 0088
+  @????: goto . <- .
+  next 0182
+block 0188
+  pred 0067
+  ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+  next 0189 *
+  next 018a
+block 0189
+  pred 0188
+  ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0130
+  next 0140
+  next 018a *
+block 018a
+  pred 0188
+  pred 0189
+  @????: goto . <- .
+  next 006a
+block 0067
+  pred 0065
+  @????: goto . <- .
+  next 0188
+block 018b
+  pred 005f
+  ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+  next 018c *
+  next 018d
+block 018c
+  pred 018b
+  ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0130
+  next 0140
+  next 018d *
+block 018d
+  pred 018b
+  pred 018c
+  @????: goto . <- .
+  next 0062
+block 005f
+  pred 003f
+  pred 0054
+  pred 005c
+  @????: goto . <- .
+  next 018b
+block 018e
+  pred 0079
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 018f *
+  next 0190
+block 018f
+  pred 018e
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 0191
+  next 0192 *
+block 0192
+  pred 018f
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0190
+block 0190
+  pred 018e
+  pred 0192
+  pred 0193
+  @????: goto . <- .
+  next 007c
+block 0191
+  pred 018f
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0193
+block 0193
+  pred 0191
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 0190
+block 0079
+  pred 0062
+  @????: goto . <- .
+  next 018e
diff --git a/dx/tests/098-dex-jsr-ret-throw/info.txt b/dx/tests/098-dex-jsr-ret-throw/info.txt
new file mode 100644
index 0000000..41636a2
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/info.txt
@@ -0,0 +1,5 @@
+The enclosed class file was generated with javac version 1.5.0_13-b05. 
+It contains an example of a subroutine being exited by a "throw" instruction in 
+such a way that it caused the frame merge and subroutine inliner
+algorithms to not converge. This was bug #1137450.
+
diff --git a/dx/tests/098-dex-jsr-ret-throw/run b/dx/tests/098-dex-jsr-ret-throw/run
new file mode 100755
index 0000000..dfc7b89
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --method=run --rop-blocks 'ViewDebug$ViewServer.class'
diff --git a/dx/tests/099-dex-core-library-error/Blort.java b/dx/tests/099-dex-core-library-error/Blort.java
new file mode 100644
index 0000000..6f619d7
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Blort.java
@@ -0,0 +1,5 @@
+package java.blort;
+
+public class Blort {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Muffins.java b/dx/tests/099-dex-core-library-error/Muffins.java
new file mode 100644
index 0000000..7ee4c4c
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Muffins.java
@@ -0,0 +1,5 @@
+package javax.net;
+
+public class Muffins {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Zorch.java b/dx/tests/099-dex-core-library-error/Zorch.java
new file mode 100644
index 0000000..57c311f
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Zorch.java
@@ -0,0 +1,5 @@
+package javax.zorch;
+
+public class Zorch {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/expected.txt b/dx/tests/099-dex-core-library-error/expected.txt
new file mode 100644
index 0000000..022871a
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/expected.txt
@@ -0,0 +1,6 @@
+javac 1.7.0-internal_bootstrap
+exit code: 1
+exit code: 1
+exit code: 0
+Found zorch.dex
+Done
diff --git a/dx/tests/099-dex-core-library-error/info.txt b/dx/tests/099-dex-core-library-error/info.txt
new file mode 100644
index 0000000..3a62267
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/info.txt
@@ -0,0 +1,3 @@
+This tests that attempts to define core classes fail and that
+an attempt to define a legal javax.* class succeeds. (Only *some*
+javax packages are considered to be off-limits.)
diff --git a/dx/tests/099-dex-core-library-error/run b/dx/tests/099-dex-core-library-error/run
new file mode 100644
index 0000000..f063266
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/run
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+
+dx --debug --dex --output=blort.dex java/blort/Blort.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r blort.dex ]; then
+    echo Found blort.dex
+fi
+
+dx --debug --dex --output=muffins.dex javax/net/Muffins.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r muffins.dex ]; then
+    echo Found muffins.dex
+fi
+
+dx --debug --dex --output=zorch.dex javax/zorch/Zorch.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r zorch.dex ]; then
+    echo Found zorch.dex
+fi
+
+echo Done
diff --git a/dx/tests/100-local-mismatch/blort1.j b/dx/tests/100-local-mismatch/blort1.j
new file mode 100644
index 0000000..327557e
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort1.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort1
+.super java/lang/Object
+
+.method public static basicTypeMismatch1()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x Ljava/lang/Object; from start to end
+    bipush 1
+    istore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort2.j b/dx/tests/100-local-mismatch/blort2.j
new file mode 100644
index 0000000..6fc79cc
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort2.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort2
+.super java/lang/Object
+
+.method public static basicTypeMismatch2()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x I from start to end
+    aconst_null
+    astore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort3.j b/dx/tests/100-local-mismatch/blort3.j
new file mode 100644
index 0000000..0fdcb89
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort3.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort3
+.super java/lang/Object
+
+.method public static arrayMismatch1()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x [B from start to end
+    bipush 1
+    istore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort4.j b/dx/tests/100-local-mismatch/blort4.j
new file mode 100644
index 0000000..1ef207d
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort4.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort4
+.super java/lang/Object
+
+.method public static arrayMismatch2()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x [Ljava/lang/Object; from start to end
+    ldc "hello"
+    astore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/expected.txt b/dx/tests/100-local-mismatch/expected.txt
new file mode 100644
index 0000000..235b206
--- /dev/null
+++ b/dx/tests/100-local-mismatch/expected.txt
@@ -0,0 +1,9 @@
+TEST 1
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type java.lang.Object. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 2
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 3
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type byte[]. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 4
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.String using a local variable of type java.lang.Object[]. This is symptomatic of .class transformation tools that ignore local variable information.
+DONE
diff --git a/dx/tests/100-local-mismatch/info.txt b/dx/tests/100-local-mismatch/info.txt
new file mode 100644
index 0000000..89b6e10
--- /dev/null
+++ b/dx/tests/100-local-mismatch/info.txt
@@ -0,0 +1,3 @@
+This is a smoke test that makes sure that dx complains when a local
+variable table entry fundamentally disagrees with an instruction that
+accesses that local.
diff --git a/dx/tests/100-local-mismatch/run b/dx/tests/100-local-mismatch/run
new file mode 100644
index 0000000..fbcf1ed
--- /dev/null
+++ b/dx/tests/100-local-mismatch/run
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort1.j >/dev/null
+jasmin -d . blort2.j >/dev/null
+jasmin -d . blort3.j >/dev/null
+jasmin -d . blort4.j >/dev/null
+
+echo "TEST 1"
+dx --dex Blort1.class 2>&1 | grep mismatch
+
+echo "TEST 2"
+dx --dex Blort2.class 2>&1 | grep mismatch
+
+echo "TEST 3"
+dx --dex Blort3.class 2>&1 | grep mismatch
+
+echo "TEST 4"
+dx --dex Blort4.class 2>&1 | grep mismatch
+
+echo "DONE"
diff --git a/dx/tests/101-verify-wide-math/expected.txt b/dx/tests/101-verify-wide-math/expected.txt
new file mode 100644
index 0000000..4bd352d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/expected.txt
@@ -0,0 +1,54 @@
+Generated: ./op_d2f.class
+d2f: expected failure occurred
+Generated: ./op_d2i.class
+d2i: expected failure occurred
+Generated: ./op_d2l.class
+d2l: expected failure occurred
+Generated: ./op_dadd.class
+dadd: expected failure occurred
+Generated: ./op_dcmpg.class
+dcmpg: expected failure occurred
+Generated: ./op_dcmpl.class
+dcmpl: expected failure occurred
+Generated: ./op_ddiv.class
+ddiv: expected failure occurred
+Generated: ./op_dmul.class
+dmul: expected failure occurred
+Generated: ./op_dneg.class
+dneg: expected failure occurred
+Generated: ./op_drem.class
+drem: expected failure occurred
+Generated: ./op_dsub.class
+dsub: expected failure occurred
+Generated: ./op_l2d.class
+l2d: expected failure occurred
+Generated: ./op_l2f.class
+l2f: expected failure occurred
+Generated: ./op_l2i.class
+l2i: expected failure occurred
+Generated: ./op_ladd.class
+ladd: expected failure occurred
+Generated: ./op_land.class
+land: expected failure occurred
+Generated: ./op_lcmp.class
+lcmp: expected failure occurred
+Generated: ./op_ldiv.class
+ldiv: expected failure occurred
+Generated: ./op_lmul.class
+lmul: expected failure occurred
+Generated: ./op_lneg.class
+lneg: expected failure occurred
+Generated: ./op_lor.class
+lor: expected failure occurred
+Generated: ./op_lrem.class
+lrem: expected failure occurred
+Generated: ./op_lshl.class
+lshl: expected failure occurred
+Generated: ./op_lshr.class
+lshr: expected failure occurred
+Generated: ./op_lsub.class
+lsub: expected failure occurred
+Generated: ./op_lushr.class
+lushr: expected failure occurred
+Generated: ./op_lxor.class
+lxor: expected failure occurred
diff --git a/dx/tests/101-verify-wide-math/info.txt b/dx/tests/101-verify-wide-math/info.txt
new file mode 100644
index 0000000..6ec551d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that wide-taking (category-2) "calculation" opcodes (math
+ops, comparisons, etc.) verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/101-verify-wide-math/op_d2f.j b/dx/tests/101-verify-wide-math/op_d2f.j
new file mode 100644
index 0000000..65a3c9d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2f
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2f
+    freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2i.j b/dx/tests/101-verify-wide-math/op_d2i.j
new file mode 100644
index 0000000..6e8976c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2i
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2i
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2l.j b/dx/tests/101-verify-wide-math/op_d2l.j
new file mode 100644
index 0000000..f8e24c9
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2l
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2l
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dadd.j b/dx/tests/101-verify-wide-math/op_dadd.j
new file mode 100644
index 0000000..232c541
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dadd
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dadd
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpg.j b/dx/tests/101-verify-wide-math/op_dcmpg.j
new file mode 100644
index 0000000..cd1b151
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpg.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dcmpg
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dcmpg
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpl.j b/dx/tests/101-verify-wide-math/op_dcmpl.j
new file mode 100644
index 0000000..dd54c52
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dcmpl
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dcmpl
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ddiv.j b/dx/tests/101-verify-wide-math/op_ddiv.j
new file mode 100644
index 0000000..b9ee329
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ddiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ddiv
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ddiv
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dmul.j b/dx/tests/101-verify-wide-math/op_dmul.j
new file mode 100644
index 0000000..f915e79
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dmul
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dmul
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dneg.j b/dx/tests/101-verify-wide-math/op_dneg.j
new file mode 100644
index 0000000..98fd9df
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dneg
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    dneg
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_drem.j b/dx/tests/101-verify-wide-math/op_drem.j
new file mode 100644
index 0000000..c0fca65
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_drem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_drem
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    drem
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dsub.j b/dx/tests/101-verify-wide-math/op_dsub.j
new file mode 100644
index 0000000..e04f505
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dsub
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dsub
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2d.j b/dx/tests/101-verify-wide-math/op_l2d.j
new file mode 100644
index 0000000..d4ac0a8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2d
+.super java/lang/Object
+
+.method public static test(I)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2d
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2f.j b/dx/tests/101-verify-wide-math/op_l2f.j
new file mode 100644
index 0000000..2dbe9d2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2f
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2f
+    freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2i.j b/dx/tests/101-verify-wide-math/op_l2i.j
new file mode 100644
index 0000000..1b4e68a
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2i
+.super java/lang/Object
+
+.method public static test(I)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2i
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ladd.j b/dx/tests/101-verify-wide-math/op_ladd.j
new file mode 100644
index 0000000..1dbb6f8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ladd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ladd
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ladd
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_land.j b/dx/tests/101-verify-wide-math/op_land.j
new file mode 100644
index 0000000..e8a55bb
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_land.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_land
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    land
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lcmp.j b/dx/tests/101-verify-wide-math/op_lcmp.j
new file mode 100644
index 0000000..b651c9c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lcmp.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lcmp
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lcmp
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ldiv.j b/dx/tests/101-verify-wide-math/op_ldiv.j
new file mode 100644
index 0000000..677daa2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ldiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ldiv
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ldiv
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lmul.j b/dx/tests/101-verify-wide-math/op_lmul.j
new file mode 100644
index 0000000..074d67c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lmul
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lmul
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lneg.j b/dx/tests/101-verify-wide-math/op_lneg.j
new file mode 100644
index 0000000..18d5780
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lneg
+.super java/lang/Object
+
+.method public static test(I)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    lneg
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lor.j b/dx/tests/101-verify-wide-math/op_lor.j
new file mode 100644
index 0000000..267ff1f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lor
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lor
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lrem.j b/dx/tests/101-verify-wide-math/op_lrem.j
new file mode 100644
index 0000000..5e0df6e
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lrem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lrem
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lrem
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshl.j b/dx/tests/101-verify-wide-math/op_lshl.j
new file mode 100644
index 0000000..bc16ea5
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lshl
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lshl
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshr.j b/dx/tests/101-verify-wide-math/op_lshr.j
new file mode 100644
index 0000000..b93fb2f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lshr
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lshr
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lsub.j b/dx/tests/101-verify-wide-math/op_lsub.j
new file mode 100644
index 0000000..823d899
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lsub
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lsub
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lushr.j b/dx/tests/101-verify-wide-math/op_lushr.j
new file mode 100644
index 0000000..aa9feb2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lushr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lushr
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lushr
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lxor.j b/dx/tests/101-verify-wide-math/op_lxor.j
new file mode 100644
index 0000000..3897c96
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lxor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lxor
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lxor
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/run b/dx/tests/101-verify-wide-math/run
new file mode 100644
index 0000000..a5ecd58
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/run
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop d2f
+oneop d2i
+oneop d2l
+oneop dadd
+oneop dcmpg
+oneop dcmpl
+oneop ddiv
+oneop dmul
+oneop dneg
+oneop drem
+oneop dsub
+oneop l2d
+oneop l2f
+oneop l2i
+oneop ladd
+oneop land
+oneop lcmp
+oneop ldiv
+oneop lmul
+oneop lneg
+oneop lor
+oneop lrem
+oneop lshl
+oneop lshr
+oneop lsub
+oneop lushr
+oneop lxor
diff --git a/dx/tests/102-verify-nonwide-math/expected.txt b/dx/tests/102-verify-nonwide-math/expected.txt
new file mode 100644
index 0000000..3f857b1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/expected.txt
@@ -0,0 +1,48 @@
+Generated: ./op_f2d.class
+f2d: expected failure occurred
+Generated: ./op_f2i.class
+f2i: expected failure occurred
+Generated: ./op_f2l.class
+f2l: expected failure occurred
+Generated: ./op_fadd.class
+fadd: expected failure occurred
+Generated: ./op_fdiv.class
+fdiv: expected failure occurred
+Generated: ./op_fmul.class
+fmul: expected failure occurred
+Generated: ./op_fneg.class
+fneg: expected failure occurred
+Generated: ./op_frem.class
+frem: expected failure occurred
+Generated: ./op_fsub.class
+fsub: expected failure occurred
+Generated: ./op_i2d.class
+i2d: expected failure occurred
+Generated: ./op_i2f.class
+i2f: expected failure occurred
+Generated: ./op_i2l.class
+i2l: expected failure occurred
+Generated: ./op_iadd.class
+iadd: expected failure occurred
+Generated: ./op_iand.class
+iand: expected failure occurred
+Generated: ./op_idiv.class
+idiv: expected failure occurred
+Generated: ./op_imul.class
+imul: expected failure occurred
+Generated: ./op_ineg.class
+ineg: expected failure occurred
+Generated: ./op_ior.class
+ior: expected failure occurred
+Generated: ./op_irem.class
+irem: expected failure occurred
+Generated: ./op_ishl.class
+ishl: expected failure occurred
+Generated: ./op_ishr.class
+ishr: expected failure occurred
+Generated: ./op_isub.class
+isub: expected failure occurred
+Generated: ./op_iushr.class
+iushr: expected failure occurred
+Generated: ./op_ixor.class
+ixor: expected failure occurred
diff --git a/dx/tests/102-verify-nonwide-math/info.txt b/dx/tests/102-verify-nonwide-math/info.txt
new file mode 100644
index 0000000..10e52ba
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that non-wide-taking (category-1) "calculation" opcodes (math
+ops, comparisons, etc.) to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/102-verify-nonwide-math/op_f2d.j b/dx/tests/102-verify-nonwide-math/op_f2d.j
new file mode 100644
index 0000000..75e8917
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2d
+.super java/lang/Object
+
+.method public static test(I)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2d
+    dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2i.j b/dx/tests/102-verify-nonwide-math/op_f2i.j
new file mode 100644
index 0000000..2d36af7
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2i
+.super java/lang/Object
+
+.method public static test(I)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2i
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2l.j b/dx/tests/102-verify-nonwide-math/op_f2l.j
new file mode 100644
index 0000000..fae9e21
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2l
+.super java/lang/Object
+
+.method public static test(I)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2l
+    lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fadd.j b/dx/tests/102-verify-nonwide-math/op_fadd.j
new file mode 100644
index 0000000..dc3743f
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fadd
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fadd
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fdiv.j b/dx/tests/102-verify-nonwide-math/op_fdiv.j
new file mode 100644
index 0000000..8609be2
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fdiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fdiv
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fdiv
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fmul.j b/dx/tests/102-verify-nonwide-math/op_fmul.j
new file mode 100644
index 0000000..fe4661c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fmul
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fmul
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fneg.j b/dx/tests/102-verify-nonwide-math/op_fneg.j
new file mode 100644
index 0000000..34898bd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fneg
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    fneg
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_frem.j b/dx/tests/102-verify-nonwide-math/op_frem.j
new file mode 100644
index 0000000..17f4602
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_frem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_frem
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    frem
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fsub.j b/dx/tests/102-verify-nonwide-math/op_fsub.j
new file mode 100644
index 0000000..692f4f8
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fsub
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fsub
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2d.j b/dx/tests/102-verify-nonwide-math/op_i2d.j
new file mode 100644
index 0000000..6c73dbe
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2d
+.super java/lang/Object
+
+.method public static test(FF)D
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2d
+    dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2f.j b/dx/tests/102-verify-nonwide-math/op_i2f.j
new file mode 100644
index 0000000..cee0b84
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2f
+.super java/lang/Object
+
+.method public static test(FF)F
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2f
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2l.j b/dx/tests/102-verify-nonwide-math/op_i2l.j
new file mode 100644
index 0000000..d6f2daa
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2l
+.super java/lang/Object
+
+.method public static test(FF)J
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2l
+    lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iadd.j b/dx/tests/102-verify-nonwide-math/op_iadd.j
new file mode 100644
index 0000000..e3d92e3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iadd
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iadd
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iand.j b/dx/tests/102-verify-nonwide-math/op_iand.j
new file mode 100644
index 0000000..063738c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iand.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iand
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iand
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_idiv.j b/dx/tests/102-verify-nonwide-math/op_idiv.j
new file mode 100644
index 0000000..2e3e3a3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_idiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_idiv
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    idiv
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_imul.j b/dx/tests/102-verify-nonwide-math/op_imul.j
new file mode 100644
index 0000000..7f4f612
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_imul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_imul
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    imul
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ineg.j b/dx/tests/102-verify-nonwide-math/op_ineg.j
new file mode 100644
index 0000000..68fcb8c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ineg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ineg
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ineg
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ior.j b/dx/tests/102-verify-nonwide-math/op_ior.j
new file mode 100644
index 0000000..c8c3a4b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ior.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ior
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ior
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_irem.j b/dx/tests/102-verify-nonwide-math/op_irem.j
new file mode 100644
index 0000000..b22f1fd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_irem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_irem
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    irem
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishl.j b/dx/tests/102-verify-nonwide-math/op_ishl.j
new file mode 100644
index 0000000..e617182
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ishl
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ishl
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishr.j b/dx/tests/102-verify-nonwide-math/op_ishr.j
new file mode 100644
index 0000000..64eba11
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ishr
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ishr
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_isub.j b/dx/tests/102-verify-nonwide-math/op_isub.j
new file mode 100644
index 0000000..ee789b0
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_isub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_isub
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    isub
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iushr.j b/dx/tests/102-verify-nonwide-math/op_iushr.j
new file mode 100644
index 0000000..73c34f1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iushr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iushr
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iushr
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ixor.j b/dx/tests/102-verify-nonwide-math/op_ixor.j
new file mode 100644
index 0000000..68f095b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ixor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ixor
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ixor
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/run b/dx/tests/102-verify-nonwide-math/run
new file mode 100644
index 0000000..eb4a294
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/run
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop f2d
+oneop f2i
+oneop f2l
+oneop fadd
+oneop fdiv
+oneop fmul
+oneop fneg
+oneop frem
+oneop fsub
+oneop i2d
+oneop i2f
+oneop i2l
+oneop iadd
+oneop iand
+oneop idiv
+oneop imul
+oneop ineg
+oneop ior
+oneop irem
+oneop ishl
+oneop ishr
+oneop isub
+oneop iushr
+oneop ixor
diff --git a/dx/tests/103-verify-branch-ops/expected.txt b/dx/tests/103-verify-branch-ops/expected.txt
new file mode 100644
index 0000000..2c96704
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/expected.txt
@@ -0,0 +1,36 @@
+Generated: ./op_if_acmpeq.class
+if_acmpeq: expected failure occurred
+Generated: ./op_if_acmpne.class
+if_acmpne: expected failure occurred
+Generated: ./op_if_icmpeq.class
+if_icmpeq: expected failure occurred
+Generated: ./op_if_icmpge.class
+if_icmpge: expected failure occurred
+Generated: ./op_if_icmpgt.class
+if_icmpgt: expected failure occurred
+Generated: ./op_if_icmple.class
+if_icmple: expected failure occurred
+Generated: ./op_if_icmplt.class
+if_icmplt: expected failure occurred
+Generated: ./op_if_icmpne.class
+if_icmpne: expected failure occurred
+Generated: ./op_ifeq.class
+ifeq: expected failure occurred
+Generated: ./op_ifge.class
+ifge: expected failure occurred
+Generated: ./op_ifgt.class
+ifgt: expected failure occurred
+Generated: ./op_ifle.class
+ifle: expected failure occurred
+Generated: ./op_iflt.class
+iflt: expected failure occurred
+Generated: ./op_ifne.class
+ifne: expected failure occurred
+Generated: ./op_ifnonnull.class
+ifnonnull: expected failure occurred
+Generated: ./op_ifnull.class
+ifnull: expected failure occurred
+Generated: ./op_lookupswitch.class
+lookupswitch: expected failure occurred
+Generated: ./op_tableswitch.class
+tableswitch: expected failure occurred
diff --git a/dx/tests/103-verify-branch-ops/info.txt b/dx/tests/103-verify-branch-ops/info.txt
new file mode 100644
index 0000000..8705e83
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/info.txt
@@ -0,0 +1,2 @@
+This tests branch opcodes to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpeq.j b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
new file mode 100644
index 0000000..4339200
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_acmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_acmpeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpne.j b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
new file mode 100644
index 0000000..57a317b
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_acmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_acmpne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpeq.j b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
new file mode 100644
index 0000000..1f141bc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpge.j b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
new file mode 100644
index 0000000..6ae2e3f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpge
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpge blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpgt.j b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
new file mode 100644
index 0000000..4e67282
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpgt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpgt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmple.j b/dx/tests/103-verify-branch-ops/op_if_icmple.j
new file mode 100644
index 0000000..3511800
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmple.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmple
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmple blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmplt.j b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
new file mode 100644
index 0000000..89527f5
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmplt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmplt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpne.j b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
new file mode 100644
index 0000000..a94faee
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifeq.j b/dx/tests/103-verify-branch-ops/op_ifeq.j
new file mode 100644
index 0000000..620e1c9
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifeq.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifge.j b/dx/tests/103-verify-branch-ops/op_ifge.j
new file mode 100644
index 0000000..c46b176
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifge.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifge
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifge blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifgt.j b/dx/tests/103-verify-branch-ops/op_ifgt.j
new file mode 100644
index 0000000..8165038
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifgt.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifgt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifgt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifle.j b/dx/tests/103-verify-branch-ops/op_ifle.j
new file mode 100644
index 0000000..758943f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifle.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifle
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifle blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_iflt.j b/dx/tests/103-verify-branch-ops/op_iflt.j
new file mode 100644
index 0000000..5091355
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_iflt.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iflt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iflt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifne.j b/dx/tests/103-verify-branch-ops/op_ifne.j
new file mode 100644
index 0000000..bb2dadc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifne.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnonnull.j b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
new file mode 100644
index 0000000..f2422f0
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifnonnull
+.super java/lang/Object
+
+.method public static test(IF)V
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    ifnonnull blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnull.j b/dx/tests/103-verify-branch-ops/op_ifnull.j
new file mode 100644
index 0000000..c253b09
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnull.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifnull
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifnull blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_lookupswitch.j b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
new file mode 100644
index 0000000..21fe8f7
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lookupswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    lookupswitch
+        0x05: t1
+        0x10: t2
+        default: t3
+t1:
+    nop
+t2:
+    nop
+t3:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_tableswitch.j b/dx/tests/103-verify-branch-ops/op_tableswitch.j
new file mode 100644
index 0000000..657feff
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_tableswitch.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_tableswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    tableswitch 0x10
+        t1
+        t2
+        default: t3
+t1:
+    nop
+t2:
+    nop
+t3:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/run b/dx/tests/103-verify-branch-ops/run
new file mode 100644
index 0000000..c9ca89f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/run
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop if_acmpeq
+oneop if_acmpne
+oneop if_icmpeq
+oneop if_icmpge
+oneop if_icmpgt
+oneop if_icmple
+oneop if_icmplt
+oneop if_icmpne
+oneop ifeq
+oneop ifge
+oneop ifgt
+oneop ifle
+oneop iflt
+oneop ifne
+oneop ifnonnull
+oneop ifnull
+oneop lookupswitch
+oneop tableswitch
diff --git a/dx/tests/104-verify-return-ops/expected.txt b/dx/tests/104-verify-return-ops/expected.txt
new file mode 100644
index 0000000..5bd9dfd
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/expected.txt
@@ -0,0 +1,22 @@
+Generated: ./op_areturn.class
+areturn: expected failure occurred
+Generated: ./op_dreturn.class
+dreturn: expected failure occurred
+Generated: ./op_freturn.class
+freturn: expected failure occurred
+Generated: ./op_ireturn.class
+ireturn: expected failure occurred
+Generated: ./op_lreturn.class
+lreturn: expected failure occurred
+Generated: ./op_sig_areturn.class
+sig_areturn: expected failure occurred
+Generated: ./op_sig_dreturn.class
+sig_dreturn: expected failure occurred
+Generated: ./op_sig_freturn.class
+sig_freturn: expected failure occurred
+Generated: ./op_sig_ireturn.class
+sig_ireturn: expected failure occurred
+Generated: ./op_sig_lreturn.class
+sig_lreturn: expected failure occurred
+Generated: ./op_sig_return.class
+sig_return: expected failure occurred
diff --git a/dx/tests/104-verify-return-ops/info.txt b/dx/tests/104-verify-return-ops/info.txt
new file mode 100644
index 0000000..1f5e634
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that return opcodes verify that their arguments are actually of
+the appropriate types and that the opcode matches the method signature.
diff --git a/dx/tests/104-verify-return-ops/op_areturn.j b/dx/tests/104-verify-return-ops/op_areturn.j
new file mode 100644
index 0000000..0b25088
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_areturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_areturn
+.super java/lang/Object
+
+.method public static test(F)Ljava/lang/Object;
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_dreturn.j b/dx/tests/104-verify-return-ops/op_dreturn.j
new file mode 100644
index 0000000..1075fe1
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_dreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dreturn
+.super java/lang/Object
+
+.method public static test(F)D
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_freturn.j b/dx/tests/104-verify-return-ops/op_freturn.j
new file mode 100644
index 0000000..6586ce6
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_freturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_freturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_ireturn.j b/dx/tests/104-verify-return-ops/op_ireturn.j
new file mode 100644
index 0000000..e93dda8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_ireturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ireturn
+.super java/lang/Object
+
+.method public static test(F)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_lreturn.j b/dx/tests/104-verify-return-ops/op_lreturn.j
new file mode 100644
index 0000000..349f353
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_lreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lreturn
+.super java/lang/Object
+
+.method public static test(F)J
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_areturn.j b/dx/tests/104-verify-return-ops/op_sig_areturn.j
new file mode 100644
index 0000000..f1ea1b4
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_areturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_areturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    aconst_null
+    areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_dreturn.j b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
new file mode 100644
index 0000000..fa6fcd2
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_dreturn
+.super java/lang/Object
+
+.method public static test(D)F
+    .limit locals 2
+    .limit stack 3
+
+    dload_0
+    dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_freturn.j b/dx/tests/104-verify-return-ops/op_sig_freturn.j
new file mode 100644
index 0000000..be5dea8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_freturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_freturn
+.super java/lang/Object
+
+.method public static test(F)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_ireturn.j b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
new file mode 100644
index 0000000..ab3aa40
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_ireturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_lreturn.j b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
new file mode 100644
index 0000000..e5a121d
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_lreturn
+.super java/lang/Object
+
+.method public static test(J)F
+    .limit locals 2
+    .limit stack 3
+
+    lload_0
+    lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_return.j b/dx/tests/104-verify-return-ops/op_sig_return.j
new file mode 100644
index 0000000..26c0678
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_return.j
@@ -0,0 +1,23 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_return
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    return
+.end method
diff --git a/dx/tests/104-verify-return-ops/run b/dx/tests/104-verify-return-ops/run
new file mode 100644
index 0000000..471d490
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/run
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop areturn
+oneop dreturn
+oneop freturn
+oneop ireturn
+oneop lreturn
+oneop sig_areturn
+oneop sig_dreturn
+oneop sig_freturn
+oneop sig_ireturn
+oneop sig_lreturn
+oneop sig_return
+
diff --git a/dx/tests/105-verify-load-store-ops/expected.txt b/dx/tests/105-verify-load-store-ops/expected.txt
new file mode 100644
index 0000000..409ead0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/expected.txt
@@ -0,0 +1,82 @@
+Generated: ./op_aaload.class
+aaload: expected failure occurred
+Generated: ./op_aastore.class
+aastore: expected failure occurred
+Generated: ./op_astore.class
+astore: expected failure occurred
+Generated: ./op_astore_0.class
+astore_0: expected failure occurred
+Generated: ./op_astore_1.class
+astore_1: expected failure occurred
+Generated: ./op_astore_2.class
+astore_2: expected failure occurred
+Generated: ./op_astore_3.class
+astore_3: expected failure occurred
+Generated: ./op_baload.class
+baload: expected failure occurred
+Generated: ./op_bastore.class
+bastore: expected failure occurred
+Generated: ./op_caload.class
+caload: expected failure occurred
+Generated: ./op_castore.class
+castore: expected failure occurred
+Generated: ./op_daload.class
+daload: expected failure occurred
+Generated: ./op_dastore.class
+dastore: expected failure occurred
+Generated: ./op_dstore.class
+dstore: expected failure occurred
+Generated: ./op_dstore_0.class
+dstore_0: expected failure occurred
+Generated: ./op_dstore_1.class
+dstore_1: expected failure occurred
+Generated: ./op_dstore_2.class
+dstore_2: expected failure occurred
+Generated: ./op_dstore_3.class
+dstore_3: expected failure occurred
+Generated: ./op_faload.class
+faload: expected failure occurred
+Generated: ./op_fastore.class
+fastore: expected failure occurred
+Generated: ./op_fstore.class
+fstore: expected failure occurred
+Generated: ./op_fstore_0.class
+fstore_0: expected failure occurred
+Generated: ./op_fstore_1.class
+fstore_1: expected failure occurred
+Generated: ./op_fstore_2.class
+fstore_2: expected failure occurred
+Generated: ./op_fstore_3.class
+fstore_3: expected failure occurred
+Generated: ./op_iaload.class
+iaload: expected failure occurred
+Generated: ./op_iastore.class
+iastore: expected failure occurred
+Generated: ./op_istore.class
+istore: expected failure occurred
+Generated: ./op_istore_0.class
+istore_0: expected failure occurred
+Generated: ./op_istore_1.class
+istore_1: expected failure occurred
+Generated: ./op_istore_2.class
+istore_2: expected failure occurred
+Generated: ./op_istore_3.class
+istore_3: expected failure occurred
+Generated: ./op_laload.class
+laload: expected failure occurred
+Generated: ./op_lastore.class
+lastore: expected failure occurred
+Generated: ./op_lstore.class
+lstore: expected failure occurred
+Generated: ./op_lstore_0.class
+lstore_0: expected failure occurred
+Generated: ./op_lstore_1.class
+lstore_1: expected failure occurred
+Generated: ./op_lstore_2.class
+lstore_2: expected failure occurred
+Generated: ./op_lstore_3.class
+lstore_3: expected failure occurred
+Generated: ./op_saload.class
+saload: expected failure occurred
+Generated: ./op_sastore.class
+sastore: expected failure occurred
diff --git a/dx/tests/105-verify-load-store-ops/info.txt b/dx/tests/105-verify-load-store-ops/info.txt
new file mode 100644
index 0000000..85b7311
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/info.txt
@@ -0,0 +1,3 @@
+This tests that load and store opcodes verify that their arguments are
+actually of the appropriate types.
+
diff --git a/dx/tests/105-verify-load-store-ops/op_aaload.j b/dx/tests/105-verify-load-store-ops/op_aaload.j
new file mode 100644
index 0000000..f77827e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aaload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_aaload
+.super java/lang/Object
+
+.method public static test([F)V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    iconst_0
+    aaload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_aastore.j b/dx/tests/105-verify-load-store-ops/op_aastore.j
new file mode 100644
index 0000000..58e1576
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_aastore
+.super java/lang/Object
+
+.method public static test([I)V
+    .limit locals 2
+    .limit stack 4
+
+    aload_0
+    iconst_0
+    aconst_null
+    aastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore.j b/dx/tests/105-verify-load-store-ops/op_astore.j
new file mode 100644
index 0000000..25131bf
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_0.j b/dx/tests/105-verify-load-store-ops/op_astore_0.j
new file mode 100644
index 0000000..b509c12
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_1.j b/dx/tests/105-verify-load-store-ops/op_astore_1.j
new file mode 100644
index 0000000..a6c1043
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_2.j b/dx/tests/105-verify-load-store-ops/op_astore_2.j
new file mode 100644
index 0000000..cb84ee8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_3.j b/dx/tests/105-verify-load-store-ops/op_astore_3.j
new file mode 100644
index 0000000..c716ba2
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_baload.j b/dx/tests/105-verify-load-store-ops/op_baload.j
new file mode 100644
index 0000000..cfcaf74
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_baload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_baload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    baload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_bastore.j b/dx/tests/105-verify-load-store-ops/op_bastore.j
new file mode 100644
index 0000000..587fcd3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_bastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_bastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    bastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_caload.j b/dx/tests/105-verify-load-store-ops/op_caload.j
new file mode 100644
index 0000000..ceaf09f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_caload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_caload
+.super java/lang/Object
+
+.method public static test(D)V
+    .limit locals 2
+    .limit stack 3
+
+    dload_0
+    iconst_0
+    caload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_castore.j b/dx/tests/105-verify-load-store-ops/op_castore.j
new file mode 100644
index 0000000..5bd493e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_castore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_castore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    castore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_daload.j b/dx/tests/105-verify-load-store-ops/op_daload.j
new file mode 100644
index 0000000..895d6be
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_daload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_daload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    daload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dastore.j b/dx/tests/105-verify-load-store-ops/op_dastore.j
new file mode 100644
index 0000000..b102f79
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    dconst_0
+    dastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore.j b/dx/tests/105-verify-load-store-ops/op_dstore.j
new file mode 100644
index 0000000..d656a84
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_0.j b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
new file mode 100644
index 0000000..cb3da3a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_1.j b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
new file mode 100644
index 0000000..45fcf9b
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_2.j b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
new file mode 100644
index 0000000..7c167d4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_3.j b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
new file mode 100644
index 0000000..17222e0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_faload.j b/dx/tests/105-verify-load-store-ops/op_faload.j
new file mode 100644
index 0000000..1c17a8e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_faload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_faload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    faload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fastore.j b/dx/tests/105-verify-load-store-ops/op_fastore.j
new file mode 100644
index 0000000..799555e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    fconst_0
+    fastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore.j b/dx/tests/105-verify-load-store-ops/op_fstore.j
new file mode 100644
index 0000000..5c61ebe
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_0.j b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
new file mode 100644
index 0000000..d3033e9
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_1.j b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
new file mode 100644
index 0000000..0abca8f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_2.j b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
new file mode 100644
index 0000000..5cd1ebc
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_3.j b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
new file mode 100644
index 0000000..a232307
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iaload.j b/dx/tests/105-verify-load-store-ops/op_iaload.j
new file mode 100644
index 0000000..3face08
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iaload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iaload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    iaload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iastore.j b/dx/tests/105-verify-load-store-ops/op_iastore.j
new file mode 100644
index 0000000..d090e37
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    iastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore.j b/dx/tests/105-verify-load-store-ops/op_istore.j
new file mode 100644
index 0000000..138d709
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_0.j b/dx/tests/105-verify-load-store-ops/op_istore_0.j
new file mode 100644
index 0000000..2644c3d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_1.j b/dx/tests/105-verify-load-store-ops/op_istore_1.j
new file mode 100644
index 0000000..03534ee
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_2.j b/dx/tests/105-verify-load-store-ops/op_istore_2.j
new file mode 100644
index 0000000..e1a80b3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_3.j b/dx/tests/105-verify-load-store-ops/op_istore_3.j
new file mode 100644
index 0000000..43c226f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_laload.j b/dx/tests/105-verify-load-store-ops/op_laload.j
new file mode 100644
index 0000000..3143604
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_laload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_laload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    laload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lastore.j b/dx/tests/105-verify-load-store-ops/op_lastore.j
new file mode 100644
index 0000000..b7ea069
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    lconst_0
+    lastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore.j b/dx/tests/105-verify-load-store-ops/op_lstore.j
new file mode 100644
index 0000000..fde6974
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_0.j b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
new file mode 100644
index 0000000..e98eab4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_1.j b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
new file mode 100644
index 0000000..0e2291a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_2.j b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
new file mode 100644
index 0000000..a84702d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    dconst_0
+    lstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_3.j b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
new file mode 100644
index 0000000..c35ace8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_saload.j b/dx/tests/105-verify-load-store-ops/op_saload.j
new file mode 100644
index 0000000..4a80939
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_saload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_saload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    saload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_sastore.j b/dx/tests/105-verify-load-store-ops/op_sastore.j
new file mode 100644
index 0000000..c97dd66
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_sastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    sastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/run b/dx/tests/105-verify-load-store-ops/run
new file mode 100644
index 0000000..adf987c
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/run
@@ -0,0 +1,68 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop aaload
+oneop aastore
+oneop astore
+oneop astore_0
+oneop astore_1
+oneop astore_2
+oneop astore_3
+oneop baload
+oneop bastore
+oneop caload
+oneop castore
+oneop daload
+oneop dastore
+oneop dstore
+oneop dstore_0
+oneop dstore_1
+oneop dstore_2
+oneop dstore_3
+oneop faload
+oneop fastore
+oneop fstore
+oneop fstore_0
+oneop fstore_1
+oneop fstore_2
+oneop fstore_3
+oneop iaload
+oneop iastore
+oneop istore
+oneop istore_0
+oneop istore_1
+oneop istore_2
+oneop istore_3
+oneop laload
+oneop lastore
+oneop lstore
+oneop lstore_0
+oneop lstore_1
+oneop lstore_2
+oneop lstore_3
+oneop saload
+oneop sastore
diff --git a/dx/tests/106-verify-object-ops/expected.txt b/dx/tests/106-verify-object-ops/expected.txt
new file mode 100644
index 0000000..ce36b7e
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/expected.txt
@@ -0,0 +1,32 @@
+Generated: ./op_anewarray.class
+anewarray: expected failure occurred
+Generated: ./op_arraylength.class
+arraylength: expected failure occurred
+Generated: ./op_athrow.class
+athrow: expected failure occurred
+Generated: ./op_checkcast.class
+checkcast: expected failure occurred
+Generated: ./op_getfield.class
+getfield: expected failure occurred
+Generated: ./op_instanceof.class
+instanceof: expected failure occurred
+Generated: ./op_invokeinterface.class
+invokeinterface: expected failure occurred
+Generated: ./op_invokespecial.class
+invokespecial: expected failure occurred
+Generated: ./op_invokestatic.class
+invokestatic: expected failure occurred
+Generated: ./op_invokevirtual.class
+invokevirtual: expected failure occurred
+Generated: ./op_monitorenter.class
+monitorenter: expected failure occurred
+Generated: ./op_monitorexit.class
+monitorexit: expected failure occurred
+Generated: ./op_multianewarray.class
+multianewarray: expected failure occurred
+Generated: ./op_newarray.class
+newarray: expected failure occurred
+Generated: ./op_putfield.class
+putfield: expected failure occurred
+Generated: ./op_putstatic.class
+putstatic: expected failure occurred
diff --git a/dx/tests/106-verify-object-ops/info.txt b/dx/tests/106-verify-object-ops/info.txt
new file mode 100644
index 0000000..85295d7
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various "objecty" opcodes verify that their
+arguments are actually of the appropriate types.
diff --git a/dx/tests/106-verify-object-ops/op_anewarray.j b/dx/tests/106-verify-object-ops/op_anewarray.j
new file mode 100644
index 0000000..348acbd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_anewarray.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_anewarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    anewarray java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_arraylength.j b/dx/tests/106-verify-object-ops/op_arraylength.j
new file mode 100644
index 0000000..df5af82
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_arraylength.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_arraylength
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    arraylength
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_athrow.j b/dx/tests/106-verify-object-ops/op_athrow.j
new file mode 100644
index 0000000..a5a5be3
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_athrow.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_athrow
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    athrow
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_checkcast.j b/dx/tests/106-verify-object-ops/op_checkcast.j
new file mode 100644
index 0000000..d921ec4
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_checkcast.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_checkcast
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    checkcast java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_getfield.j b/dx/tests/106-verify-object-ops/op_getfield.j
new file mode 100644
index 0000000..4d5f782
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_getfield.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_getfield
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    getfield blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_instanceof.j b/dx/tests/106-verify-object-ops/op_instanceof.j
new file mode 100644
index 0000000..8a938f5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_instanceof.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_instanceof
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    instanceof java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokeinterface.j b/dx/tests/106-verify-object-ops/op_invokeinterface.j
new file mode 100644
index 0000000..2f1528f
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokeinterface.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokeinterface
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokeinterface blort/x()V 1
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokespecial.j b/dx/tests/106-verify-object-ops/op_invokespecial.j
new file mode 100644
index 0000000..87baffc
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokespecial.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokespecial
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokespecial blort/x()V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokestatic.j b/dx/tests/106-verify-object-ops/op_invokestatic.j
new file mode 100644
index 0000000..80247bd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokestatic.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokestatic
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokestatic blort/x(I)V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokevirtual.j b/dx/tests/106-verify-object-ops/op_invokevirtual.j
new file mode 100644
index 0000000..d7ba9b5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokevirtual.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokevirtual
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokevirtual blort/x()V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorenter.j b/dx/tests/106-verify-object-ops/op_monitorenter.j
new file mode 100644
index 0000000..95e23d8
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorenter.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_monitorenter
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    monitorenter
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorexit.j b/dx/tests/106-verify-object-ops/op_monitorexit.j
new file mode 100644
index 0000000..50e5fe2
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorexit.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_monitorexit
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    monitorexit
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_multianewarray.j b/dx/tests/106-verify-object-ops/op_multianewarray.j
new file mode 100644
index 0000000..d02f474
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_multianewarray.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_multianewarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    iconst_0
+    multianewarray [[[I 2
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_newarray.j b/dx/tests/106-verify-object-ops/op_newarray.j
new file mode 100644
index 0000000..16ab256
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_newarray.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_newarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    newarray short
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putfield.j b/dx/tests/106-verify-object-ops/op_putfield.j
new file mode 100644
index 0000000..eb33fc9
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putfield.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_putfield
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    iconst_0
+    putfield blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putstatic.j b/dx/tests/106-verify-object-ops/op_putstatic.j
new file mode 100644
index 0000000..221f08b
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putstatic.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_putstatic
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    putstatic blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/run b/dx/tests/106-verify-object-ops/run
new file mode 100644
index 0000000..f512210
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop anewarray
+oneop arraylength
+oneop athrow
+oneop checkcast
+oneop getfield
+oneop instanceof
+oneop invokeinterface
+oneop invokespecial
+oneop invokestatic
+oneop invokevirtual
+oneop monitorenter
+oneop monitorexit
+oneop multianewarray
+oneop newarray
+oneop putfield
+oneop putstatic
diff --git a/dx/tests/107-verify-stack-ops/expected.txt b/dx/tests/107-verify-stack-ops/expected.txt
new file mode 100644
index 0000000..812025d
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/expected.txt
@@ -0,0 +1,34 @@
+Generated: ./op_dup.class
+dup: expected failure occurred
+Generated: ./op_dup_x1_case1.class
+dup_x1_case1: expected failure occurred
+Generated: ./op_dup_x1_case2.class
+dup_x1_case2: expected failure occurred
+Generated: ./op_dup_x2_case1.class
+dup_x2_case1: expected failure occurred
+Generated: ./op_dup_x2_case2.class
+dup_x2_case2: expected failure occurred
+Generated: ./op_dup_x2_case3.class
+dup_x2_case3: expected failure occurred
+Generated: ./op_dup2.class
+dup2: expected failure occurred
+Generated: ./op_dup2_x1_case1.class
+dup2_x1_case1: expected failure occurred
+Generated: ./op_dup2_x1_case2.class
+dup2_x1_case2: expected failure occurred
+Generated: ./op_dup2_x1_case3.class
+dup2_x1_case3: expected failure occurred
+Generated: ./op_dup2_x2_case1.class
+dup2_x2_case1: expected failure occurred
+Generated: ./op_dup2_x2_case2.class
+dup2_x2_case2: expected failure occurred
+Generated: ./op_dup2_x2_case3.class
+dup2_x2_case3: expected failure occurred
+Generated: ./op_pop.class
+pop: expected failure occurred
+Generated: ./op_pop2.class
+pop2: expected failure occurred
+Generated: ./op_swap_case1.class
+swap_case1: expected failure occurred
+Generated: ./op_swap_case2.class
+swap_case2: expected failure occurred
diff --git a/dx/tests/107-verify-stack-ops/info.txt b/dx/tests/107-verify-stack-ops/info.txt
new file mode 100644
index 0000000..c489ace
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various stack manipulation opcodes verify that their
+arguments are actually of the appropriate categories.
diff --git a/dx/tests/107-verify-stack-ops/op_dup.j b/dx/tests/107-verify-stack-ops/op_dup.j
new file mode 100644
index 0000000..6c9ebc2
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    dup
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2.j b/dx/tests/107-verify-stack-ops/op_dup2.j
new file mode 100644
index 0000000..d17ce20
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
new file mode 100644
index 0000000..7b014f7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
new file mode 100644
index 0000000..26667f0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    iconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
new file mode 100644
index 0000000..35e97c4
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    dconst_0
+    iconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
new file mode 100644
index 0000000..d15ccc3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    dconst_0
+    dconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
new file mode 100644
index 0000000..e2538a0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    iconst_0
+    iconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
new file mode 100644
index 0000000..1e2645c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    iconst_0
+    dconst_0
+    iconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
new file mode 100644
index 0000000..dad31e5
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    dconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
new file mode 100644
index 0000000..037b0f8
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    dup_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
new file mode 100644
index 0000000..fa52b16
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
new file mode 100644
index 0000000..7c4e89c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    iconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
new file mode 100644
index 0000000..c4aa545
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    iconst_0
+    dconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
new file mode 100644
index 0000000..f920d8c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    dconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop.j b/dx/tests/107-verify-stack-ops/op_pop.j
new file mode 100644
index 0000000..1b74e2c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    pop
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2.j b/dx/tests/107-verify-stack-ops/op_pop2.j
new file mode 100644
index 0000000..ef2d122
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    pop2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2_case2.j b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
new file mode 100644
index 0000000..f9d6ea3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    pop2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case1.j b/dx/tests/107-verify-stack-ops/op_swap_case1.j
new file mode 100644
index 0000000..f6b4ab7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_swap_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    swap
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case2.j b/dx/tests/107-verify-stack-ops/op_swap_case2.j
new file mode 100644
index 0000000..6e0fcba
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_swap_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    swap
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/run b/dx/tests/107-verify-stack-ops/run
new file mode 100644
index 0000000..a55c639
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop dup
+oneop dup_x1_case1
+oneop dup_x1_case2
+oneop dup_x2_case1
+oneop dup_x2_case2
+oneop dup_x2_case3
+oneop dup2
+oneop dup2_x1_case1
+oneop dup2_x1_case2
+oneop dup2_x1_case3
+oneop dup2_x2_case1
+oneop dup2_x2_case2
+oneop dup2_x2_case3
+oneop pop
+oneop pop2
+oneop swap_case1
+oneop swap_case2
diff --git a/dx/tests/108-string-annotation/Blort.java b/dx/tests/108-string-annotation/Blort.java
new file mode 100644
index 0000000..9bb52e4
--- /dev/null
+++ b/dx/tests/108-string-annotation/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+    @Frotz(name = "grue")
+    public static void testSingle() {
+        // This space intentionally left blank.
+    }
+
+    @Fizmo(names = "gruesome")
+    public static void testArray1() {
+        // This space intentionally left blank.
+    }
+
+    @Fizmo(names = {"awful", "awesome"})
+    public static void testArray2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/108-string-annotation/Fizmo.java b/dx/tests/108-string-annotation/Fizmo.java
new file mode 100644
index 0000000..a20bba0
--- /dev/null
+++ b/dx/tests/108-string-annotation/Fizmo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public @interface Fizmo {
+    String[] names();
+}
diff --git a/dx/tests/108-string-annotation/Frotz.java b/dx/tests/108-string-annotation/Frotz.java
new file mode 100644
index 0000000..3ad5426
--- /dev/null
+++ b/dx/tests/108-string-annotation/Frotz.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public @interface Frotz {
+    String name();
+}
diff --git a/dx/tests/108-string-annotation/expected.txt b/dx/tests/108-string-annotation/expected.txt
new file mode 100644
index 0000000..befa690
--- /dev/null
+++ b/dx/tests/108-string-annotation/expected.txt
@@ -0,0 +1,13 @@
+javac 1.7.0-internal_bootstrap
+
+  elements[0]:
+    name
+    value: string "grue"
+
+  elements[0]:
+    names
+    value: array {"gruesome"}
+
+  elements[0]:
+    names
+    value: array {"awful", "awesome"}
diff --git a/dx/tests/108-string-annotation/info.txt b/dx/tests/108-string-annotation/info.txt
new file mode 100644
index 0000000..6c85834
--- /dev/null
+++ b/dx/tests/108-string-annotation/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+string annotations get represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/108-string-annotation/run b/dx/tests/108-string-annotation/run
new file mode 100644
index 0000000..d89053f
--- /dev/null
+++ b/dx/tests/108-string-annotation/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java Fizmo.java Frotz.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-to=- *.class | cut -f 2 -d '|' | awk '
+
+BEGIN {
+    dumping = 0;
+}
+
+/annotation$/ {
+    dumping = 1;
+    printf("\n");
+    next;
+}
+
+/^[ ]*$/ {
+    dumping = 0;
+    next;
+}
+
+dumping && /^  elements/ {
+    print;
+}
+
+dumping && /^    name_idx/ {
+    printf("    %s\n", $4);
+}
+
+dumping && /^    value/ {
+    print;
+}
+'
diff --git a/dx/tests/109-int-branch/blort.j b/dx/tests/109-int-branch/blort.j
new file mode 100644
index 0000000..8517177
--- /dev/null
+++ b/dx/tests/109-int-branch/blort.j
@@ -0,0 +1,99 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public static test1(ZBCSI[I)V
+    .limit locals 6
+    .limit stack 3
+
+    iload_0
+    iload_1
+    if_icmpeq zorch
+
+    iload_2
+    iload_3
+    if_icmpne zorch
+
+    iload 4
+    aload 5
+    iconst_0
+    iaload
+    if_icmplt zorch
+
+    aload 5
+    iconst_0
+    iaload
+    iload_0
+    if_icmpgt zorch
+
+    iload 4
+    iload_1
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    return
+.end method
+
+.method public static test2(I)Ljava/lang/Object;
+    .limit locals 2
+    .limit stack 3
+
+    aconst_null
+    astore 1
+
+    aload_1
+    iconst_0
+    iaload
+    iload_0
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    aconst_null
+    areturn
+.end method
+
+.method public static test3(I[I)Ljava/lang/Object;
+    .limit locals 3
+    .limit stack 3
+
+    aconst_null
+    astore 2
+
+frotz:
+    aload_2
+    ifnonnull fizmo
+
+    aload_1
+    astore_2
+    goto frotz
+
+fizmo:
+    aload_2
+    iconst_0
+    iaload
+    iload_0
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    aconst_null
+    areturn
+.end method
diff --git a/dx/tests/109-int-branch/expected.txt b/dx/tests/109-int-branch/expected.txt
new file mode 100644
index 0000000..d73bfa0
--- /dev/null
+++ b/dx/tests/109-int-branch/expected.txt
@@ -0,0 +1,67 @@
+Generated: ./blort.class
+blort.test1:(ZBCSI[I)V:
+regs: 000f; ins: 0006; outs: 0000
+  0000: move v0, v9
+  0001: move v1, v10
+  0002: move v2, v11
+  0003: move v3, v12
+  0004: move v4, v13
+  0005: move-object v5, v14
+  0006: move v6, v0
+  0007: move v7, v1
+  0008: if-eq v6, v7, 0021 // +0019
+  000a: move v6, v2
+  000b: move v7, v3
+  000c: if-ne v6, v7, 0021 // +0015
+  000e: move v6, v4
+  000f: move-object v7, v5
+  0010: const/4 v8, #int 0 // #0
+  0011: aget v7, v7, v8
+  0013: if-lt v6, v7, 0021 // +000e
+  0015: move-object v6, v5
+  0016: const/4 v7, #int 0 // #0
+  0017: aget v6, v6, v7
+  0019: move v7, v0
+  001a: if-gt v6, v7, 0021 // +0007
+  001c: move v6, v4
+  001d: move v7, v1
+  001e: if-ge v6, v7, 0021 // +0003
+  0020: nop
+  0021: return-void
+  source file: "blort.j"
+blort.test2:(I)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move v0, v4
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move-object v2, v1
+  0004: const/4 v3, #int 0 // #0
+  0005: aget-object v2, v2, v3
+  0007: move v3, v0
+  0008: if-ge v2, v3, 000b // +0003
+  000a: nop
+  000b: const/4 v2, #null // #0
+  000c: move-object v0, v2
+  000d: return-object v0
+  source file: "blort.j"
+blort.test3:(I[I)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move v0, v5
+  0001: move-object v1, v6
+  0002: const/4 v3, #null // #0
+  0003: move-object v2, v3
+  0004: move-object v3, v2
+  0005: if-nez v3, 000a // +0005
+  0007: move-object v3, v1
+  0008: move-object v2, v3
+  0009: goto 0004 // -0005
+  000a: move-object v3, v2
+  000b: const/4 v4, #int 0 // #0
+  000c: aget v3, v3, v4
+  000e: move v4, v0
+  000f: if-ge v3, v4, 0012 // +0003
+  0011: nop
+  0012: const/4 v3, #null // #0
+  0013: move-object v0, v3
+  0014: return-object v0
+  source file: "blort.j"
diff --git a/dx/tests/109-int-branch/info.txt b/dx/tests/109-int-branch/info.txt
new file mode 100644
index 0000000..e650a12
--- /dev/null
+++ b/dx/tests/109-int-branch/info.txt
@@ -0,0 +1,6 @@
+This tests that an int branch with valid arguments is properly translated.
+(Regression test.)
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/109-int-branch/run b/dx/tests/109-int-branch/run
new file mode 100644
index 0000000..68b17ea
--- /dev/null
+++ b/dx/tests/109-int-branch/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dex --no-optimize --dump-method="blort.test*" blort.class
diff --git a/dx/tests/run-all-tests b/dx/tests/run-all-tests
new file mode 100755
index 0000000..1ade761
--- /dev/null
+++ b/dx/tests/run-all-tests
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+passed=0
+failed=0
+failNames=""
+
+for i in *; do
+    if [ -d "$i" -a -r "$i" ]; then
+        ./run-test "$i"
+        if [ "$?" = "0" ]; then
+            ((passed += 1))
+        else
+            ((failed += 1))
+            failNames="$failNames $i"
+        fi
+    fi
+done
+
+echo "passed: $passed test(s)"
+echo "failed: $failed test(s)"
+
+for i in $failNames; do
+    echo "failed: $i"
+done
+    
diff --git a/dx/tests/run-test b/dx/tests/run-test
new file mode 100755
index 0000000..84eadb9
--- /dev/null
+++ b/dx/tests/run-test
@@ -0,0 +1,146 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+export JAVAC="${progdir}/../../../prebuilt/common/openjdk/bin/javac -version"
+
+info="info.txt"
+run="run"
+expected="expected.txt"
+output="out.txt"
+
+dev_mode="no"
+if [ "x$1" = "x--dev" ]; then
+    dev_mode="yes"
+    shift
+fi
+
+update_mode="no"
+if [ "x$1" = "x--update" ]; then
+    update_mode="yes"
+    shift
+fi
+
+usage="no"
+if [ "x$1" = "x--help" ]; then
+    usage="yes"
+else
+    if [ "x$1" = "x" ]; then
+        testdir=`basename "$oldwd"`
+    else
+        testdir="$1"
+    fi
+
+    if [ '!' -d "$testdir" ]; then
+        td2=`echo ${testdir}-*`
+        if [ '!' -d "$td2" ]; then
+            echo "${testdir}: no such test directory" 1>&2
+            usage="yes"
+        fi
+        testdir="$td2"
+    fi
+fi
+
+if [ "$usage" = "yes" ]; then
+    prog=`basename $prog`
+    (
+        echo "usage:"
+        echo "  $prog --help             Print this message."
+        echo "  $prog testname           Run test normally."
+        echo "  $prog --dev testname     Development mode (dump to stdout)."
+        echo "  $prog --update testname  Update mode (replace expected.txt)."
+        echo "  Omitting the test name uses the current directory as the test."
+    ) 1>&2
+    exit 1
+fi
+
+td_info="$testdir"/"$info"
+td_run="$testdir"/"$run"
+td_expected="$testdir"/"$expected"
+
+tmpdir=/tmp/test-$$
+
+if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
+    echo "${testdir}: missing files" 1>&2
+    exit 1
+fi
+
+# copy the test to a temp dir and run it
+
+echo "${testdir}: running..." 1>&2
+
+rm -rf "$tmpdir"
+cp -Rp "$testdir" "$tmpdir"
+cd "$tmpdir"
+chmod 755 "$run"
+
+#PATH="${progdir}/../build/bin:${PATH}"
+
+good="no"
+if [ "$dev_mode" = "yes" ]; then
+    "./$run" 2>&1
+    echo "exit status: $?" 1>&2
+    good="yes"
+elif [ "$update_mode" = "yes" ]; then
+    "./$run" >"${progdir}/$td_expected" 2>&1
+    good="yes"
+else
+    "./$run" >"$output" 2>&1
+    cmp -s "$expected" "$output"
+    if [ "$?" = "0" ]; then
+        # output == expected
+        good="yes"
+        echo "$testdir"': succeeded!' 1>&2
+    fi
+fi
+
+if [ "$good" = "yes" ]; then
+    cd "$oldwd"
+    rm -rf "$tmpdir"
+    exit 0
+fi
+
+(
+    echo "${testdir}: FAILED!"
+    echo ' '
+    echo '#################### info'
+    cat "$info" | sed 's/^/# /g'
+    echo '#################### diffs'
+    diff -u "$expected" "$output"
+    echo '####################'
+    echo ' '
+    echo "files left in $tmpdir"
+) 1>&2
+
+exit 1