Snapshot 1319ed83a94fd506e54a43d6cef8b6bb2295c525
Change-Id: Ifeff1cc0867b7a1459e6d83cb354883f134fda74
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..adde173
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,65 @@
+# Copyright (C) 2006 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ libdex \
+ vm \
+ dalvikvm \
+ dexgen \
+ dexlist \
+ dexopt \
+ dexdump \
+ dx \
+ tools \
+ unit-tests \
+ ))
+
+include $(subdirs)
+
+
+.PHONY: dex dex-debug
+ifeq ($(DONT_INSTALL_DEX_FILES),true)
+dex:
+ @echo "Forcing a remake with DONT_INSTALL_DEX_FILES=false"
+ $(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false
+else
+# DONT_INSTALL_DEX_FILES is already false, so a normal make takes care of it.
+dex: $(DEFAULT_GOAL)
+endif
+
+d :=
+ifneq ($(GENERATE_DEX_DEBUG),)
+d := debug
+endif
+ifneq ($(DONT_INSTALL_DEX_FILES),true)
+d := $(d)-install
+endif
+ifneq ($(d),debug-install)
+# generate the debug .dex files, with a copy in ./dalvik/DEBUG-FILES.
+# We need to rebuild the .dex files for the debug output to be generated.
+# The "touch -c $(DX)" is a hack that we know will force
+# a rebuild of the .dex files. If $(DX) doesn't exist yet,
+# we won't touch it (-c) and the normal build will create
+# the .dex files naturally.
+dex-debug:
+ @echo "Forcing an app rebuild with GENERATE_DEX_DEBUG=true"
+ @touch -c $(DX)
+ $(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false GENERATE_DEX_DEBUG=true
+else
+# GENERATE_DEX_DEBUG and DONT_INSTALL_DEX_FILES are already set properly,
+# so a normal make takes care of it.
+dex-debug: $(DEFAULT_GOAL)
+endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..3fbdc64
--- /dev/null
+++ b/CleanSpec.mk
@@ -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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..4760004
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,223 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Android-specific code. ==
+ =========================================================================
+
+ Copyright (c) 2005-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.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ =========================================================================
+ == NOTICE file for the x86 JIT libenc subdirectory. ==
+ =========================================================================
+
+Apache Harmony
+Copyright 2006, 2010 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Harmony were originally developed by
+Intel Corporation and are licensed to the Apache Software
+Foundation under the "Software Grant and Corporate Contribution
+License Agreement" and for which the following copyright notices
+apply
+ (C) Copyright 2005 Intel Corporation
+ (C) Copyright 2005-2006 Intel Corporation
+ (C) Copyright 2006 Intel Corporation
+
+Portions of the Apache Portable Runtime used by DRLVM were
+developed at the National Center for Supercomputing Applications
+(NCSA) at the University of Illinois at Urbana-Champaign.
+
+This software contains code derived from the RSA Data Security
+Inc. MD5 Message-Digest Algorithm.
+
+This software contains code derived from UNIX V7, Copyright(C)
+Caldera International Inc.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..2969180
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,60 @@
+This directory contains the Dalvik virtual machine and core class library,
+as well as related tools, libraries, and tests.
+
+A note about the licenses and header comments
+---------------------------------------------
+
+Much of the code under this directory originally came from the Apache
+Harmony project, and as such contains the standard Apache header
+comment. Some of the code was written originally for the Android
+project, and as such contains the standard Android header comment.
+Some files contain code from both projects. In these cases, the header
+comment is a combination of the other two, and the portions of the
+code from Harmony are identified as indicated in the comment.
+
+Here is the combined header comment:
+
+/*
+ * Copyright (C) <year> The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----------
+ *
+ * Portions of the code surrounded by "// BEGIN Harmony code" and
+ * "// END Harmony code" are copyrighted and licensed separately, as
+ * follows:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+Native SH call bridge
+---------------------
+
+Native SH call bridge is written by
+Shin-ichiro KAWASAKI <shinichiro.kawasaki.mg@hitachi.com>
+and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp.
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
new file mode 100644
index 0000000..734f6f5
--- /dev/null
+++ b/dalvikvm/Android.mk
@@ -0,0 +1,75 @@
+# 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.
+
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Common definitions.
+#
+
+dalvikvm_src_files := \
+ Main.cpp
+
+dalvikvm_c_includes := \
+ $(JNI_H_INCLUDE) \
+ dalvik/include
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(dalvikvm_src_files)
+LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+LOCAL_SHARED_LIBRARIES := \
+ libdvm \
+ libssl \
+ libz
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dalvikvm
+
+include $(BUILD_EXECUTABLE)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(dalvikvm_src_files)
+ LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+ ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
+ # OS X comes with all these libraries, so there is no need
+ # to build any of them. Note: OpenSSL consists of libssl
+ # and libcrypto.
+ LOCAL_LDLIBS := -lffi -lssl -lcrypto -lz
+ else
+ LOCAL_LDLIBS += -ldl -lpthread
+ LOCAL_SHARED_LIBRARIES += libdvm libcrypto libicuuc libicui18n libssl
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := dalvikvm
+
+ include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/dalvikvm/Main.cpp b/dalvikvm/Main.cpp
new file mode 100644
index 0000000..4aa6e20
--- /dev/null
+++ b/dalvikvm/Main.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ */
+/*
+ * Command-line invocation of the Dalvik VM.
+ */
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+
+
+/*
+ * We want failed write() calls to just return with an error.
+ */
+static void blockSigpipe()
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGPIPE);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+ fprintf(stderr, "WARNING: SIGPIPE not blocked\n");
+}
+
+/*
+ * Create a String[] and populate it with the contents of argv.
+ */
+static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc)
+{
+ jclass stringClass = NULL;
+ jobjectArray strArray = NULL;
+ jobjectArray result = NULL;
+ int i;
+
+ stringClass = env->FindClass("java/lang/String");
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while finding class String\n");
+ goto bail;
+ }
+ assert(stringClass != NULL);
+ strArray = env->NewObjectArray(argc, stringClass, NULL);
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while creating String array\n");
+ goto bail;
+ }
+ assert(strArray != NULL);
+
+ for (i = 0; i < argc; i++) {
+ jstring argStr;
+
+ argStr = env->NewStringUTF(argv[i]);
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while allocating Strings\n");
+ goto bail;
+ }
+ assert(argStr != NULL);
+ env->SetObjectArrayElement(strArray, i, argStr);
+ env->DeleteLocalRef(argStr);
+ }
+
+ /* return the array, and ensure we don't delete the local ref to it */
+ result = strArray;
+ strArray = NULL;
+
+bail:
+ env->DeleteLocalRef(stringClass);
+ env->DeleteLocalRef(strArray);
+ return result;
+}
+
+/*
+ * Determine whether or not the specified method is public.
+ *
+ * Returns JNI_TRUE on success, JNI_FALSE on failure.
+ */
+static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId)
+{
+ static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC
+ jobject refMethod = NULL;
+ jclass methodClass = NULL;
+ jmethodID getModifiersId;
+ int modifiers;
+ int result = JNI_FALSE;
+
+ refMethod = env->ToReflectedMethod(clazz, methodId, JNI_FALSE);
+ if (refMethod == NULL) {
+ fprintf(stderr, "Dalvik VM unable to get reflected method\n");
+ goto bail;
+ }
+
+ /*
+ * We now have a Method instance. We need to call
+ * its getModifiers() method.
+ */
+ methodClass = env->FindClass("java/lang/reflect/Method");
+ if (methodClass == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find class Method\n");
+ goto bail;
+ }
+ getModifiersId = env->GetMethodID(methodClass,
+ "getModifiers", "()I");
+ if (getModifiersId == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n");
+ goto bail;
+ }
+
+ modifiers = env->CallIntMethod(refMethod, getModifiersId);
+ if ((modifiers & PUBLIC) == 0) {
+ fprintf(stderr, "Dalvik VM: main() is not public\n");
+ goto bail;
+ }
+
+ result = JNI_TRUE;
+
+bail:
+ env->DeleteLocalRef(refMethod);
+ env->DeleteLocalRef(methodClass);
+ return result;
+}
+
+/*
+ * Parse arguments. Most of it just gets passed through to the VM. The
+ * JNI spec defines a handful of standard arguments.
+ */
+int main(int argc, char* const argv[])
+{
+ JavaVM* vm = NULL;
+ JNIEnv* env = NULL;
+ JavaVMInitArgs initArgs;
+ JavaVMOption* options = NULL;
+ char* slashClass = NULL;
+ int optionCount, curOpt, i, argIdx;
+ int needExtra = JNI_FALSE;
+ int result = 1;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /* ignore argv[0] */
+ argv++;
+ argc--;
+
+ /*
+ * If we're adding any additional stuff, e.g. function hook specifiers,
+ * add them to the count here.
+ *
+ * We're over-allocating, because this includes the options to the VM
+ * plus the options to the program.
+ */
+ optionCount = argc;
+
+ options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount);
+ memset(options, 0, sizeof(JavaVMOption) * optionCount);
+
+ /*
+ * Copy options over. Everything up to the name of the class starts
+ * with a '-' (the function hook stuff is strictly internal).
+ *
+ * [Do we need to catch & handle "-jar" here?]
+ */
+ for (curOpt = argIdx = 0; argIdx < argc; argIdx++) {
+ if (argv[argIdx][0] != '-' && !needExtra)
+ break;
+ options[curOpt++].optionString = strdup(argv[argIdx]);
+
+ /* some options require an additional arg */
+ needExtra = JNI_FALSE;
+ if (strcmp(argv[argIdx], "-classpath") == 0 ||
+ strcmp(argv[argIdx], "-cp") == 0)
+ /* others? */
+ {
+ needExtra = JNI_TRUE;
+ }
+ }
+
+ if (needExtra) {
+ fprintf(stderr, "Dalvik VM requires value after last option flag\n");
+ goto bail;
+ }
+
+ /* insert additional internal options here */
+
+ assert(curOpt <= optionCount);
+
+ initArgs.version = JNI_VERSION_1_4;
+ initArgs.options = options;
+ initArgs.nOptions = curOpt;
+ initArgs.ignoreUnrecognized = JNI_FALSE;
+
+ //printf("nOptions = %d\n", initArgs.nOptions);
+
+ blockSigpipe();
+
+ /*
+ * Start VM. The current thread becomes the main thread of the VM.
+ */
+ if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {
+ fprintf(stderr, "Dalvik VM init failed (check log file)\n");
+ goto bail;
+ }
+
+ /*
+ * Make sure they provided a class name. We do this after VM init
+ * so that things like "-Xrunjdwp:help" have the opportunity to emit
+ * a usage statement.
+ */
+ if (argIdx == argc) {
+ fprintf(stderr, "Dalvik VM requires a class name\n");
+ goto bail;
+ }
+
+ /*
+ * We want to call main() with a String array with our arguments in it.
+ * Create an array and populate it. Note argv[0] is not included.
+ */
+ jobjectArray strArray;
+ strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1);
+ if (strArray == NULL)
+ goto bail;
+
+ /*
+ * Find [class].main(String[]).
+ */
+ jclass startClass;
+ jmethodID startMeth;
+ char* cp;
+
+ /* convert "com.android.Blah" to "com/android/Blah" */
+ slashClass = strdup(argv[argIdx]);
+ for (cp = slashClass; *cp != '\0'; cp++)
+ if (*cp == '.')
+ *cp = '/';
+
+ startClass = env->FindClass(slashClass);
+ if (startClass == NULL) {
+ fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
+ goto bail;
+ }
+
+ startMeth = env->GetStaticMethodID(startClass,
+ "main", "([Ljava/lang/String;)V");
+ if (startMeth == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n",
+ slashClass);
+ goto bail;
+ }
+
+ /*
+ * Make sure the method is public. JNI doesn't prevent us from calling
+ * a private method, so we have to check it explicitly.
+ */
+ if (!methodIsPublic(env, startClass, startMeth))
+ goto bail;
+
+ /*
+ * Invoke main().
+ */
+ env->CallStaticVoidMethod(startClass, startMeth, strArray);
+
+ if (!env->ExceptionCheck())
+ result = 0;
+
+bail:
+ /*printf("Shutting down Dalvik VM\n");*/
+ if (vm != NULL) {
+ /*
+ * This allows join() and isAlive() on the main thread to work
+ * correctly, and also provides uncaught exception handling.
+ */
+ if (vm->DetachCurrentThread() != JNI_OK) {
+ fprintf(stderr, "Warning: unable to detach main thread\n");
+ result = 1;
+ }
+
+ if (vm->DestroyJavaVM() != 0)
+ fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n");
+ /*printf("\nDalvik VM has exited\n");*/
+ }
+
+ for (i = 0; i < optionCount; i++)
+ free((char*) options[i].optionString);
+ free(options);
+ free(slashClass);
+ /*printf("--- VM is down, process exiting\n");*/
+ return result;
+}
diff --git a/dexdump/Android.mk b/dexdump/Android.mk
new file mode 100644
index 0000000..7c22b60
--- /dev/null
+++ b/dexdump/Android.mk
@@ -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.
+
+#
+# dexdump, similar in purpose to objdump.
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+ DexDump.cpp
+
+dexdump_c_includes := \
+ dalvik \
+ $(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+ libdex
+
+##
+##
+## Build the device command line tool dexdump
+##
+##
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries) libz liblog
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_LDLIBS +=
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdexdump_static
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host command line tool dexdump
+##
+##
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) liblog
+
+ifneq ($(strip $(USE_MINGW)),)
+LOCAL_STATIC_LIBRARIES += libz
+else
+LOCAL_LDLIBS += -lpthread -lz
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexdump/DexDump.cpp b/dexdump/DexDump.cpp
new file mode 100644
index 0000000..0d7b425
--- /dev/null
+++ b/dexdump/DexDump.cpp
@@ -0,0 +1,1915 @@
+/*
+ * 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.
+ */
+
+/*
+ * The "dexdump" tool is intended to mimic "objdump". When possible, use
+ * similar command-line arguments.
+ *
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ * nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class<?>"
+ * - class shows declared fields and methods; does not show inherited fields
+ */
+
+#include "libdex/DexFile.h"
+
+#include "libdex/CmdUtils.h"
+#include "libdex/DexCatch.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexProto.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/SysUtil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+static const char* gProgName = "dexdump";
+
+enum OutputFormat {
+ OUTPUT_PLAIN = 0, /* default */
+ OUTPUT_XML, /* fancy */
+};
+
+/* command-line options */
+struct Options {
+ bool checksumOnly;
+ bool disassemble;
+ bool showFileHeaders;
+ bool showSectionHeaders;
+ bool ignoreBadChecksum;
+ bool dumpRegisterMaps;
+ OutputFormat outputFormat;
+ const char* tempFileName;
+ bool exportsOnly;
+ bool verbose;
+};
+
+struct Options gOptions;
+
+/* basic info about a field or method */
+struct FieldMethodInfo {
+ const char* classDescriptor;
+ const char* name;
+ const char* signature;
+};
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static inline u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static inline u4 get4LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
+}
+
+/*
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form. For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]". Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
+ */
+static char* descriptorToDot(const char* str)
+{
+ int targetLen = strlen(str);
+ int offset = 0;
+ int arrayDepth = 0;
+ char* newStr;
+
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && str[offset] == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ /* primitive type */
+ str = primitiveTypeLabel(str[offset]);
+ offset = 0;
+ targetLen = strlen(str);
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && str[offset] == 'L' &&
+ str[offset+targetLen-1] == ';')
+ {
+ targetLen -= 2;
+ offset++;
+ }
+ }
+
+ newStr = (char*)malloc(targetLen + arrayDepth * 2 +1);
+
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = str[offset + i];
+ newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+ }
+
+ /* add the appropriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ newStr[i++] = '[';
+ newStr[i++] = ']';
+ }
+ newStr[i] = '\0';
+ assert(i == targetLen + arrayDepth * 2);
+
+ return newStr;
+}
+
+/*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+ const char* lastSlash;
+ char* newStr;
+ char* cp;
+
+ /* reduce to just the class name, trimming trailing ';' */
+ lastSlash = strrchr(str, '/');
+ if (lastSlash == NULL)
+ lastSlash = str + 1; /* start past 'L' */
+ else
+ lastSlash++; /* start past '/' */
+
+ newStr = strdup(lastSlash);
+ newStr[strlen(lastSlash)-1] = '\0';
+ for (cp = newStr; *cp != '\0'; cp++) {
+ if (*cp == '$')
+ *cp = '.';
+ }
+
+ return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+ if (val)
+ return "\"true\"";
+ else
+ return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+ if ((accessFlags & ACC_PUBLIC) != 0)
+ return "\"public\"";
+ else if ((accessFlags & ACC_PROTECTED) != 0)
+ return "\"protected\"";
+ else if ((accessFlags & ACC_PRIVATE) != 0)
+ return "\"private\"";
+ else
+ return "\"package\"";
+}
+
+/*
+ * Count the number of '1' bits in a word.
+ */
+static int countOnes(u4 val)
+{
+ int count = 0;
+
+ val = val - ((val >> 1) & 0x55555555);
+ val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+ count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
+
+ return count;
+}
+
+/*
+ * Flag for use with createAccessFlagStr().
+ */
+enum AccessFor {
+ kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
+ kAccessForMAX
+};
+
+/*
+ * Create a new string with human-readable access flags.
+ *
+ * In the base language the access_flags fields are type u2; in Dalvik
+ * they're u4.
+ */
+static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
+{
+#define NUM_FLAGS 18
+ static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
+ {
+ /* class, inner class */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "?", /* 0x0040 */
+ "?", /* 0x0080 */
+ "?", /* 0x0100 */
+ "INTERFACE", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "ANNOTATION", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "VERIFIED", /* 0x10000 */
+ "OPTIMIZED", /* 0x20000 */
+ },
+ {
+ /* method */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "SYNCHRONIZED", /* 0x0020 */
+ "BRIDGE", /* 0x0040 */
+ "VARARGS", /* 0x0080 */
+ "NATIVE", /* 0x0100 */
+ "?", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "STRICT", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "?", /* 0x4000 */
+ "MIRANDA", /* 0x8000 */
+ "CONSTRUCTOR", /* 0x10000 */
+ "DECLARED_SYNCHRONIZED", /* 0x20000 */
+ },
+ {
+ /* field */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "VOLATILE", /* 0x0040 */
+ "TRANSIENT", /* 0x0080 */
+ "?", /* 0x0100 */
+ "?", /* 0x0200 */
+ "?", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "?", /* 0x10000 */
+ "?", /* 0x20000 */
+ },
+ };
+ const int kLongest = 21; /* strlen of longest string above */
+ int i, count;
+ char* str;
+ char* cp;
+
+ /*
+ * Allocate enough storage to hold the expected number of strings,
+ * plus a space between each. We over-allocate, using the longest
+ * string above as the base metric.
+ */
+ count = countOnes(flags);
+ cp = str = (char*) malloc(count * (kLongest+1) +1);
+
+ for (i = 0; i < NUM_FLAGS; i++) {
+ if (flags & 0x01) {
+ const char* accessStr = kAccessStrings[forWhat][i];
+ int len = strlen(accessStr);
+ if (cp != str)
+ *cp++ = ' ';
+
+ memcpy(cp, accessStr, len);
+ cp += len;
+ }
+ flags >>= 1;
+ }
+ *cp = '\0';
+
+ return str;
+}
+
+
+/*
+ * Copy character data from "data" to "out", converting non-ASCII values
+ * to printf format chars or an ASCII filler ('.' or '?').
+ *
+ * The output buffer must be able to hold (2*len)+1 bytes. The result is
+ * NUL-terminated.
+ */
+static void asciify(char* out, const unsigned char* data, size_t len)
+{
+ while (len--) {
+ if (*data < 0x20) {
+ /* could do more here, but we don't need them yet */
+ switch (*data) {
+ case '\0':
+ *out++ = '\\';
+ *out++ = '0';
+ break;
+ case '\n':
+ *out++ = '\\';
+ *out++ = 'n';
+ break;
+ default:
+ *out++ = '.';
+ break;
+ }
+ } else if (*data >= 0x80) {
+ *out++ = '?';
+ } else {
+ *out++ = *data;
+ }
+ data++;
+ }
+ *out = '\0';
+}
+
+/*
+ * Dump the file header.
+ */
+void dumpFileHeader(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ const DexHeader* pHeader = pDexFile->pHeader;
+ char sanitized[sizeof(pHeader->magic)*2 +1];
+
+ assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
+
+ if (pOptHeader != NULL) {
+ printf("Optimized DEX file header:\n");
+
+ asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("dex_offset : %d (0x%06x)\n",
+ pOptHeader->dexOffset, pOptHeader->dexOffset);
+ printf("dex_length : %d\n", pOptHeader->dexLength);
+ printf("deps_offset : %d (0x%06x)\n",
+ pOptHeader->depsOffset, pOptHeader->depsOffset);
+ printf("deps_length : %d\n", pOptHeader->depsLength);
+ printf("opt_offset : %d (0x%06x)\n",
+ pOptHeader->optOffset, pOptHeader->optOffset);
+ printf("opt_length : %d\n", pOptHeader->optLength);
+ printf("flags : %08x\n", pOptHeader->flags);
+ printf("checksum : %08x\n", pOptHeader->checksum);
+ printf("\n");
+ }
+
+ printf("DEX file header:\n");
+ asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("checksum : %08x\n", pHeader->checksum);
+ printf("signature : %02x%02x...%02x%02x\n",
+ pHeader->signature[0], pHeader->signature[1],
+ pHeader->signature[kSHA1DigestLen-2],
+ pHeader->signature[kSHA1DigestLen-1]);
+ printf("file_size : %d\n", pHeader->fileSize);
+ printf("header_size : %d\n", pHeader->headerSize);
+ printf("link_size : %d\n", pHeader->linkSize);
+ printf("link_off : %d (0x%06x)\n",
+ pHeader->linkOff, pHeader->linkOff);
+ printf("string_ids_size : %d\n", pHeader->stringIdsSize);
+ printf("string_ids_off : %d (0x%06x)\n",
+ pHeader->stringIdsOff, pHeader->stringIdsOff);
+ printf("type_ids_size : %d\n", pHeader->typeIdsSize);
+ printf("type_ids_off : %d (0x%06x)\n",
+ pHeader->typeIdsOff, pHeader->typeIdsOff);
+ printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
+ printf("field_ids_off : %d (0x%06x)\n",
+ pHeader->fieldIdsOff, pHeader->fieldIdsOff);
+ printf("method_ids_size : %d\n", pHeader->methodIdsSize);
+ printf("method_ids_off : %d (0x%06x)\n",
+ pHeader->methodIdsOff, pHeader->methodIdsOff);
+ printf("class_defs_size : %d\n", pHeader->classDefsSize);
+ printf("class_defs_off : %d (0x%06x)\n",
+ pHeader->classDefsOff, pHeader->classDefsOff);
+ printf("data_size : %d\n", pHeader->dataSize);
+ printf("data_off : %d (0x%06x)\n",
+ pHeader->dataOff, pHeader->dataOff);
+ printf("\n");
+}
+
+/*
+ * Dump the "table of contents" for the opt area.
+ */
+void dumpOptDirectory(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ if (pOptHeader == NULL)
+ return;
+
+ printf("OPT section contents:\n");
+
+ const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
+
+ if (*pOpt == 0) {
+ printf("(1.0 format, only class lookup table is present)\n\n");
+ return;
+ }
+
+ /*
+ * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
+ * length, then the data. Chunks start on 64-bit boundaries.
+ */
+ while (*pOpt != kDexChunkEnd) {
+ const char* verboseStr;
+
+ u4 size = *(pOpt+1);
+
+ switch (*pOpt) {
+ case kDexChunkClassLookup:
+ verboseStr = "class lookup hash table";
+ break;
+ case kDexChunkRegisterMaps:
+ verboseStr = "register maps";
+ break;
+ default:
+ verboseStr = "(unknown chunk type)";
+ break;
+ }
+
+ printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
+ *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
+ verboseStr, size);
+
+ size = (size + 8 + 7) & ~7;
+ pOpt += size / sizeof(u4);
+ }
+ printf("\n");
+}
+
+/*
+ * Dump a class_def_item.
+ */
+void dumpClassDef(DexFile* pDexFile, int idx)
+{
+ const DexClassDef* pClassDef;
+ const u1* pEncodedData;
+ DexClassData* pClassData;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ return;
+ }
+
+ printf("Class #%d header:\n", idx);
+ printf("class_idx : %d\n", pClassDef->classIdx);
+ printf("access_flags : %d (0x%04x)\n",
+ pClassDef->accessFlags, pClassDef->accessFlags);
+ printf("superclass_idx : %d\n", pClassDef->superclassIdx);
+ printf("interfaces_off : %d (0x%06x)\n",
+ pClassDef->interfacesOff, pClassDef->interfacesOff);
+ printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
+ printf("annotations_off : %d (0x%06x)\n",
+ pClassDef->annotationsOff, pClassDef->annotationsOff);
+ printf("class_data_off : %d (0x%06x)\n",
+ pClassDef->classDataOff, pClassDef->classDataOff);
+ printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
+ printf("instance_fields_size: %d\n",
+ pClassData->header.instanceFieldsSize);
+ printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
+ printf("virtual_methods_size: %d\n",
+ pClassData->header.virtualMethodsSize);
+ printf("\n");
+
+ free(pClassData);
+}
+
+/*
+ * Dump an interface that a class declares to implement.
+ */
+void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
+ int i)
+{
+ const char* interfaceName =
+ dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : '%s'\n", i, interfaceName);
+ } else {
+ char* dotted = descriptorToDot(interfaceName);
+ printf("<implements name=\"%s\">\n</implements>\n", dotted);
+ free(dotted);
+ }
+}
+
+/*
+ * Dump the catches table associated with the code.
+ */
+void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
+{
+ u4 triesSize = pCode->triesSize;
+
+ if (triesSize == 0) {
+ printf(" catches : (none)\n");
+ return;
+ }
+
+ printf(" catches : %d\n", triesSize);
+
+ const DexTry* pTries = dexGetTries(pCode);
+ u4 i;
+
+ for (i = 0; i < triesSize; i++) {
+ const DexTry* pTry = &pTries[i];
+ u4 start = pTry->startAddr;
+ u4 end = start + pTry->insnCount;
+ DexCatchIterator iterator;
+
+ printf(" 0x%04x - 0x%04x\n", start, end);
+
+ dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ const char* descriptor;
+
+ if (handler == NULL) {
+ break;
+ }
+
+ descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
+ dexStringByTypeIdx(pDexFile, handler->typeIdx);
+
+ printf(" %s -> 0x%04x\n", descriptor,
+ handler->address);
+ }
+ }
+}
+
+static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+ printf(" 0x%04x line=%d\n", address, lineNum);
+ return 0;
+}
+
+/*
+ * Dump the positions list.
+ */
+void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" positions : \n");
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
+}
+
+static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature)
+{
+ printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
+ startAddress, endAddress, reg, name, descriptor,
+ signature);
+}
+
+/*
+ * Dump the locals list.
+ */
+void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" locals : \n");
+
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
+}
+
+/*
+ * Get information about a method.
+ */
+bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
+{
+ const DexMethodId* pMethodId;
+
+ if (methodIdx >= pDexFile->pHeader->methodIdsSize)
+ return false;
+
+ pMethodId = dexGetMethodId(pDexFile, methodIdx);
+ pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
+ pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ pMethInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+ return true;
+}
+
+/*
+ * Get information about a field.
+ */
+bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
+{
+ const DexFieldId* pFieldId;
+
+ if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
+ return false;
+
+ pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+ pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
+ pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ pFieldInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+ return true;
+}
+
+
+/*
+ * Look up a class' descriptor.
+ */
+const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
+{
+ return dexStringByTypeIdx(pDexFile, classIdx);
+}
+
+/*
+ * Helper for dumpInstruction(), which builds the string
+ * representation for the index in the given instruction. This will
+ * first try to use the given buffer, but if the result won't fit,
+ * then this will allocate a new buffer to hold the result. A pointer
+ * to the buffer which holds the full result is always returned, and
+ * this can be compared with the one passed in, to see if the result
+ * needs to be free()d.
+ */
+static char* indexString(DexFile* pDexFile,
+ const DecodedInstruction* pDecInsn, char* buf, size_t bufSize)
+{
+ int outSize;
+ u4 index;
+ u4 width;
+
+ /* TODO: Make the index *always* be in field B, to simplify this code. */
+ switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
+ case kFmt20bc:
+ case kFmt21c:
+ case kFmt35c:
+ case kFmt35ms:
+ case kFmt3rc:
+ case kFmt3rms:
+ case kFmt35mi:
+ case kFmt3rmi:
+ index = pDecInsn->vB;
+ width = 4;
+ break;
+ case kFmt31c:
+ index = pDecInsn->vB;
+ width = 8;
+ break;
+ case kFmt22c:
+ case kFmt22cs:
+ index = pDecInsn->vC;
+ width = 4;
+ break;
+ default:
+ index = 0;
+ width = 4;
+ break;
+ }
+
+ switch (pDecInsn->indexType) {
+ case kIndexUnknown:
+ /*
+ * This function shouldn't ever get called for this type, but do
+ * something sensible here, just to help with debugging.
+ */
+ outSize = snprintf(buf, bufSize, "<unknown-index>");
+ break;
+ case kIndexNone:
+ /*
+ * This function shouldn't ever get called for this type, but do
+ * something sensible here, just to help with debugging.
+ */
+ outSize = snprintf(buf, bufSize, "<no-index>");
+ break;
+ case kIndexVaries:
+ /*
+ * This one should never show up in a dexdump, so no need to try
+ * to get fancy here.
+ */
+ outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x",
+ width, index);
+ break;
+ case kIndexTypeRef:
+ if (index < pDexFile->pHeader->typeIdsSize) {
+ outSize = snprintf(buf, bufSize, "%s // type@%0*x",
+ getClassDescriptor(pDexFile, index), width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index);
+ }
+ break;
+ case kIndexStringRef:
+ if (index < pDexFile->pHeader->stringIdsSize) {
+ outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x",
+ dexStringById(pDexFile, index), width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<string?> // string@%0*x",
+ width, index);
+ }
+ break;
+ case kIndexMethodRef:
+ {
+ FieldMethodInfo methInfo;
+ if (getMethodInfo(pDexFile, index, &methInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
+ methInfo.classDescriptor, methInfo.name,
+ methInfo.signature, width, index);
+ free((void *) methInfo.signature);
+ } else {
+ outSize = snprintf(buf, bufSize, "<method?> // method@%0*x",
+ width, index);
+ }
+ }
+ break;
+ case kIndexFieldRef:
+ {
+ FieldMethodInfo fieldInfo;
+ if (getFieldInfo(pDexFile, index, &fieldInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
+ fieldInfo.classDescriptor, fieldInfo.name,
+ fieldInfo.signature, width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<field?> // field@%0*x",
+ width, index);
+ }
+ }
+ break;
+ case kIndexInlineMethod:
+ outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x",
+ width, index, width, index);
+ break;
+ case kIndexVtableOffset:
+ outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
+ width, index, width, index);
+ break;
+ case kIndexFieldOffset:
+ outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
+ break;
+ default:
+ outSize = snprintf(buf, bufSize, "<?>");
+ break;
+ }
+
+ if (outSize >= (int) bufSize) {
+ /*
+ * The buffer wasn't big enough; allocate and retry. Note:
+ * snprintf() doesn't count the '\0' as part of its returned
+ * size, so we add explicit space for it here.
+ */
+ outSize++;
+ buf = (char*)malloc(outSize);
+ if (buf == NULL) {
+ return NULL;
+ }
+ return indexString(pDexFile, pDecInsn, buf, outSize);
+ } else {
+ return buf;
+ }
+}
+
+/*
+ * Dump a single instruction.
+ */
+void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
+ int insnWidth, const DecodedInstruction* pDecInsn)
+{
+ char indexBufChars[200];
+ char *indexBuf = indexBufChars;
+ const u2* insns = pCode->insns;
+ int i;
+
+ printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
+ for (i = 0; i < 8; i++) {
+ if (i < insnWidth) {
+ if (i == 7) {
+ printf(" ... ");
+ } else {
+ /* print 16-bit value in little-endian order */
+ const u1* bytePtr = (const u1*) &insns[insnIdx+i];
+ printf(" %02x%02x", bytePtr[0], bytePtr[1]);
+ }
+ } else {
+ fputs(" ", stdout);
+ }
+ }
+
+ if (pDecInsn->opcode == OP_NOP) {
+ u2 instr = get2LE((const u1*) &insns[insnIdx]);
+ if (instr == kPackedSwitchSignature) {
+ printf("|%04x: packed-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kSparseSwitchSignature) {
+ printf("|%04x: sparse-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kArrayDataSignature) {
+ printf("|%04x: array-data (%d units)",
+ insnIdx, insnWidth);
+ } else {
+ printf("|%04x: nop // spacer", insnIdx);
+ }
+ } else {
+ printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode));
+ }
+
+ if (pDecInsn->indexType != kIndexNone) {
+ indexBuf = indexString(pDexFile, pDecInsn,
+ indexBufChars, sizeof(indexBufChars));
+ }
+
+ switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
+ case kFmt10x: // op
+ break;
+ case kFmt12x: // op vA, vB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt11n: // op vA, #+B
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
+ break;
+ case kFmt11x: // op vAA
+ printf(" v%d", pDecInsn->vA);
+ break;
+ case kFmt10t: // op +AA
+ case kFmt20t: // op +AAAA
+ {
+ s4 targ = (s4) pDecInsn->vA;
+ printf(" %04x // %c%04x",
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22x: // op vAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt21t: // op vAA, +BBBB
+ {
+ s4 targ = (s4) pDecInsn->vB;
+ printf(" v%d, %04x // %c%04x", pDecInsn->vA,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt21s: // op vAA, #+BBBB
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
+ break;
+ case kFmt21h: // op vAA, #+BBBB0000[00000000]
+ // The printed format varies a bit based on the actual opcode.
+ if (pDecInsn->opcode == OP_CONST_HIGH16) {
+ s4 value = pDecInsn->vB << 16;
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ } else {
+ s8 value = ((s8) pDecInsn->vB) << 48;
+ printf(" v%d, #long %lld // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ }
+ break;
+ case kFmt21c: // op vAA, thing@BBBB
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ printf(" v%d, %s", pDecInsn->vA, indexBuf);
+ break;
+ case kFmt23x: // op vAA, vBB, vCC
+ printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ printf(" v%d, v%d, #int %d // #%02x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
+ break;
+ case kFmt22t: // op vA, vB, +CCCC
+ {
+ s4 targ = (s4) pDecInsn->vC;
+ printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22s: // op vA, vB, #+CCCC
+ printf(" v%d, v%d, #int %d // #%04x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf);
+ break;
+ case kFmt30t:
+ printf(" #%08x", pDecInsn->vA);
+ break;
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ {
+ /* this is often, but not always, a float */
+ union {
+ float f;
+ u4 i;
+ } conv;
+ conv.i = pDecInsn->vB;
+ printf(" v%d, #float %f // #%08x",
+ pDecInsn->vA, conv.f, pDecInsn->vB);
+ }
+ break;
+ case kFmt31t: // op vAA, offset +BBBBBBBB
+ printf(" v%d, %08x // +%08x",
+ pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
+ break;
+ case kFmt32x: // op vAAAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
+ case kFmt35ms: // [opt] invoke-virtual+super
+ case kFmt35mi: // [opt] inline invoke
+ {
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->arg[i]);
+ else
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ case kFmt3rmi: // [opt] execute-inline/range
+ {
+ /*
+ * This doesn't match the "dx" output when some of the args are
+ * 64-bit values -- dx only shows the first register.
+ */
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->vC + i);
+ else
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ {
+ /* this is often, but not always, a double */
+ union {
+ double d;
+ u8 j;
+ } conv;
+ conv.j = pDecInsn->vB_wide;
+ printf(" v%d, #double %f // #%016llx",
+ pDecInsn->vA, conv.d, pDecInsn->vB_wide);
+ }
+ break;
+ case kFmt00x: // unknown op or breakpoint
+ break;
+ default:
+ printf(" ???");
+ break;
+ }
+
+ putchar('\n');
+
+ if (indexBuf != indexBufChars) {
+ free(indexBuf);
+ }
+}
+
+/*
+ * Dump a bytecode disassembly.
+ */
+void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+ const u2* insns;
+ int insnIdx;
+ FieldMethodInfo methInfo;
+ int startAddr;
+ char* className = NULL;
+
+ assert(pCode->insnsSize > 0);
+ insns = pCode->insns;
+
+ getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
+ startAddr = ((u1*)pCode - pDexFile->baseAddr);
+ className = descriptorToDot(methInfo.classDescriptor);
+
+ printf("%06x: |[%06x] %s.%s:%s\n",
+ startAddr, startAddr,
+ className, methInfo.name, methInfo.signature);
+ free((void *) methInfo.signature);
+
+ insnIdx = 0;
+ while (insnIdx < (int) pCode->insnsSize) {
+ int insnWidth;
+ DecodedInstruction decInsn;
+ u2 instr;
+
+ /*
+ * Note: This code parallels the function
+ * dexGetWidthFromInstruction() in InstrUtils.c, but this version
+ * can deal with data in either endianness.
+ *
+ * TODO: Figure out if this really matters, and possibly change
+ * this to just use dexGetWidthFromInstruction().
+ */
+ instr = get2LE((const u1*)insns);
+ if (instr == kPackedSwitchSignature) {
+ insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
+ } else if (instr == kSparseSwitchSignature) {
+ insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
+ } else if (instr == kArrayDataSignature) {
+ int width = get2LE((const u1*)(insns+1));
+ int size = get2LE((const u1*)(insns+2)) |
+ (get2LE((const u1*)(insns+3))<<16);
+ // The plus 1 is to round up for odd size and width.
+ insnWidth = 4 + ((size * width) + 1) / 2;
+ } else {
+ Opcode opcode = dexOpcodeFromCodeUnit(instr);
+ insnWidth = dexGetWidthFromOpcode(opcode);
+ if (insnWidth == 0) {
+ fprintf(stderr,
+ "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
+ break;
+ }
+ }
+
+ dexDecodeInstruction(insns, &decInsn);
+ dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
+
+ insns += insnWidth;
+ insnIdx += insnWidth;
+ }
+
+ free(className);
+}
+
+/*
+ * Dump a "code" struct.
+ */
+void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+
+ printf(" registers : %d\n", pCode->registersSize);
+ printf(" ins : %d\n", pCode->insSize);
+ printf(" outs : %d\n", pCode->outsSize);
+ printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
+
+ if (gOptions.disassemble)
+ dumpBytecodes(pDexFile, pDexMethod);
+
+ dumpCatches(pDexFile, pCode);
+ /* both of these are encoded in debug info */
+ dumpPositions(pDexFile, pCode, pDexMethod);
+ dumpLocals(pDexFile, pCode, pDexMethod);
+}
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
+{
+ const DexMethodId* pMethodId;
+ const char* backDescriptor;
+ const char* name;
+ char* typeDescriptor = NULL;
+ char* accessStr = NULL;
+
+ if (gOptions.exportsOnly &&
+ (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ accessStr = createAccessFlagStr(pDexMethod->accessFlags,
+ kAccessForMethod);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pDexMethod->accessFlags, accessStr);
+
+ if (pDexMethod->codeOff == 0) {
+ printf(" code : (none)\n");
+ } else {
+ printf(" code -\n");
+ dumpCode(pDexFile, pDexMethod);
+ }
+
+ if (gOptions.disassemble)
+ putchar('\n');
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ bool constructor = (name[0] == '<');
+
+ if (constructor) {
+ char* tmp;
+
+ tmp = descriptorClassToDot(backDescriptor);
+ printf("<constructor name=\"%s\"\n", tmp);
+ free(tmp);
+
+ tmp = descriptorToDot(backDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+ } else {
+ printf("<method name=\"%s\"\n", name);
+
+ const char* returnType = strrchr(typeDescriptor, ')');
+ if (returnType == NULL) {
+ fprintf(stderr, "bad method type descriptor '%s'\n",
+ typeDescriptor);
+ goto bail;
+ }
+
+ char* tmp = descriptorToDot(returnType+1);
+ printf(" return=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" abstract=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" native=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+ bool isSync =
+ (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+ (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ printf(" synchronized=%s\n", quotedBool(isSync));
+ }
+
+ printf(" static=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pDexMethod->accessFlags));
+
+ printf(">\n");
+
+ /*
+ * Parameters.
+ */
+ if (typeDescriptor[0] != '(') {
+ fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ goto bail;
+ }
+
+ char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
+ int argNum = 0;
+
+ const char* base = typeDescriptor+1;
+
+ while (*base != ')') {
+ char* cp = tmpBuf;
+
+ while (*base == '[')
+ *cp++ = *base++;
+
+ if (*base == 'L') {
+ /* copy through ';' */
+ do {
+ *cp = *base++;
+ } while (*cp++ != ';');
+ } else {
+ /* primitive char, copy it */
+ if (strchr("ZBCSIFJD", *base) == NULL) {
+ fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ goto bail;
+ }
+ *cp++ = *base++;
+ }
+
+ /* null terminate and display */
+ *cp++ = '\0';
+
+ char* tmp = descriptorToDot(tmpBuf);
+ printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+ argNum++, tmp);
+ free(tmp);
+ }
+
+ if (constructor)
+ printf("</constructor>\n");
+ else
+ printf("</method>\n");
+ }
+
+bail:
+ free(typeDescriptor);
+ free(accessStr);
+}
+
+/*
+ * Dump a static (class) field.
+ */
+void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
+{
+ const DexFieldId* pFieldId;
+ const char* backDescriptor;
+ const char* name;
+ const char* typeDescriptor;
+ char* accessStr;
+
+ if (gOptions.exportsOnly &&
+ (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
+ name = dexStringById(pDexFile, pFieldId->nameIdx);
+ typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+
+ accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pSField->accessFlags, accessStr);
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* tmp;
+
+ printf("<field name=\"%s\"\n", name);
+
+ tmp = descriptorToDot(typeDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" transient=%s\n",
+ quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+ printf(" volatile=%s\n",
+ quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+ // "value=" not knowable w/o parsing annotations
+ printf(" static=%s\n",
+ quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pSField->accessFlags));
+ printf(">\n</field>\n");
+ }
+
+ free(accessStr);
+}
+
+/*
+ * Dump an instance field.
+ */
+void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
+{
+ dumpSField(pDexFile, pIField, i);
+}
+
+/*
+ * Dump the class.
+ *
+ * Note "idx" is a DexClassDef index, not a DexTypeId index.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
+ */
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
+{
+ const DexTypeList* pInterfaces;
+ const DexClassDef* pClassDef;
+ DexClassData* pClassData = NULL;
+ const u1* pEncodedData;
+ const char* fileName;
+ const char* classDescriptor;
+ const char* superclassDescriptor;
+ char* accessStr = NULL;
+ int i;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+
+ if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+ //printf("<!-- omitting non-public class %s -->\n",
+ // classDescriptor);
+ goto bail;
+ }
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ printf("Trouble reading class data (#%d)\n", idx);
+ goto bail;
+ }
+
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /*
+ * For the XML output, show the package name. Ideally we'd gather
+ * up the classes, sort them, and dump them alphabetically so the
+ * package name wouldn't jump around, but that's not a great plan
+ * for something that needs to run on the device.
+ */
+ if (!(classDescriptor[0] == 'L' &&
+ classDescriptor[strlen(classDescriptor)-1] == ';'))
+ {
+ /* arrays and primitives should not be defined explicitly */
+ fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ /* keep going? */
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* mangle;
+ char* lastSlash;
+ char* cp;
+
+ mangle = strdup(classDescriptor + 1);
+ mangle[strlen(mangle)-1] = '\0';
+
+ /* reduce to just the package name */
+ lastSlash = strrchr(mangle, '/');
+ if (lastSlash != NULL) {
+ *lastSlash = '\0';
+ } else {
+ *mangle = '\0';
+ }
+
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == '/')
+ *cp = '.';
+ }
+
+ if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+ /* start of a new package */
+ if (*pLastPackage != NULL)
+ printf("</package>\n");
+ printf("<package name=\"%s\"\n>\n", mangle);
+ free(*pLastPackage);
+ *pLastPackage = mangle;
+ } else {
+ free(mangle);
+ }
+ }
+
+ accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
+
+ if (pClassDef->superclassIdx == kDexNoIndex) {
+ superclassDescriptor = NULL;
+ } else {
+ superclassDescriptor =
+ dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf("Class #%d -\n", idx);
+ printf(" Class descriptor : '%s'\n", classDescriptor);
+ printf(" Access flags : 0x%04x (%s)\n",
+ pClassDef->accessFlags, accessStr);
+
+ if (superclassDescriptor != NULL)
+ printf(" Superclass : '%s'\n", superclassDescriptor);
+
+ printf(" Interfaces -\n");
+ } else {
+ char* tmp;
+
+ tmp = descriptorClassToDot(classDescriptor);
+ printf("<class name=\"%s\"\n", tmp);
+ free(tmp);
+
+ if (superclassDescriptor != NULL) {
+ tmp = descriptorToDot(superclassDescriptor);
+ printf(" extends=\"%s\"\n", tmp);
+ free(tmp);
+ }
+ printf(" abstract=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" static=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pClassDef->accessFlags));
+ printf(">\n");
+ }
+ pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
+ if (pInterfaces != NULL) {
+ for (i = 0; i < (int) pInterfaces->size; i++)
+ dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Static fields -\n");
+ for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
+ dumpSField(pDexFile, &pClassData->staticFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Instance fields -\n");
+ for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
+ dumpIField(pDexFile, &pClassData->instanceFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Direct methods -\n");
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->directMethods[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Virtual methods -\n");
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
+ }
+
+ // TODO: Annotations.
+
+ if (pClassDef->sourceFileIdx != kDexNoIndex)
+ fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+ else
+ fileName = "unknown";
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" source_file_idx : %d (%s)\n",
+ pClassDef->sourceFileIdx, fileName);
+ printf("\n");
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("</class>\n");
+ }
+
+bail:
+ free(pClassData);
+ free(accessStr);
+}
+
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline const u1* align32(const u1* ptr)
+{
+ return (u1*) (((uintptr_t) ptr + 3) & ~0x03);
+}
+
+
+/*
+ * Dump a map in the "differential" format.
+ *
+ * TODO: show a hex dump of the compressed data. (We can show the
+ * uncompressed data if we move the compression code to libdex; otherwise
+ * it's too complex to merit a fast & fragile implementation here.)
+ */
+void dumpDifferentialCompressedMap(const u1** pData)
+{
+ const u1* data = *pData;
+ const u1* dataStart = data -1; // format byte already removed
+ u1 regWidth;
+ u2 numEntries;
+
+ /* standard header */
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ /* compressed data begins with the compressed data length */
+ int compressedLen = readUnsignedLeb128(&data);
+ int addrWidth = 1;
+ if ((*data & 0x80) != 0)
+ addrWidth++;
+
+ int origLen = 4 + (addrWidth + regWidth) * numEntries;
+ int compLen = (data - dataStart) + compressedLen;
+
+ printf(" (differential compression %d -> %d [%d -> %d])\n",
+ origLen, compLen,
+ (addrWidth + regWidth) * numEntries, compressedLen);
+
+ /* skip past end of entry */
+ data += compressedLen;
+
+ *pData = data;
+}
+
+/*
+ * Dump register map contents of the current method.
+ *
+ * "*pData" should point to the start of the register map data. Advances
+ * "*pData" to the start of the next map.
+ */
+void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
+ const u1** pData)
+{
+ const u1* data = *pData;
+ const DexMethodId* pMethodId;
+ const char* name;
+ int offset = data - (u1*) pDexFile->pOptHeader;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ printf(" #%d: 0x%08x %s\n", idx, offset, name);
+
+ u1 format;
+ int addrWidth;
+
+ format = *data++;
+ if (format == 1) { /* kRegMapFormatNone */
+ /* no map */
+ printf(" (no map)\n");
+ addrWidth = 0;
+ } else if (format == 2) { /* kRegMapFormatCompact8 */
+ addrWidth = 1;
+ } else if (format == 3) { /* kRegMapFormatCompact16 */
+ addrWidth = 2;
+ } else if (format == 4) { /* kRegMapFormatDifferential */
+ dumpDifferentialCompressedMap(&data);
+ goto bail;
+ } else {
+ printf(" (unknown format %d!)\n", format);
+ /* don't know how to skip data; failure will cascade to end of class */
+ goto bail;
+ }
+
+ if (addrWidth > 0) {
+ u1 regWidth;
+ u2 numEntries;
+ int idx, addr, byte;
+
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ for (idx = 0; idx < numEntries; idx++) {
+ addr = *data++;
+ if (addrWidth > 1)
+ addr |= (*data++) << 8;
+
+ printf(" %4x:", addr);
+ for (byte = 0; byte < regWidth; byte++) {
+ printf(" %02x", *data++);
+ }
+ printf("\n");
+ }
+ }
+
+bail:
+ //if (addrWidth >= 0)
+ // *pData = align32(data);
+ *pData = data;
+}
+
+/*
+ * Dump the contents of the register map area.
+ *
+ * These are only present in optimized DEX files, and the structure is
+ * not really exposed to other parts of the VM itself. We're going to
+ * dig through them here, but this is pretty fragile. DO NOT rely on
+ * this or derive other code from it.
+ */
+void dumpRegisterMaps(DexFile* pDexFile)
+{
+ const u1* pClassPool = (const u1*)pDexFile->pRegisterMapPool;
+ const u4* classOffsets;
+ const u1* ptr;
+ u4 numClasses;
+ int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
+ int idx;
+
+ if (pClassPool == NULL) {
+ printf("No register maps found\n");
+ return;
+ }
+
+ ptr = pClassPool;
+ numClasses = get4LE(ptr);
+ ptr += sizeof(u4);
+ classOffsets = (const u4*) ptr;
+
+ printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
+ printf("Maps for %d classes\n", numClasses);
+ for (idx = 0; idx < (int) numClasses; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
+ baseFileOffset + classOffsets[idx], classDescriptor);
+
+ if (classOffsets[idx] == 0)
+ continue;
+
+ /*
+ * What follows is a series of RegisterMap entries, one for every
+ * direct method, then one for every virtual method.
+ */
+ DexClassData* pClassData;
+ const u1* pEncodedData;
+ const u1* data = (u1*) pClassPool + classOffsets[idx];
+ u2 methodCount;
+ int i;
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ continue;
+ }
+
+ methodCount = *data++;
+ methodCount |= (*data++) << 8;
+ data += 2; /* two pad bytes follow methodCount */
+ if (methodCount != pClassData->header.directMethodsSize
+ + pClassData->header.virtualMethodsSize)
+ {
+ printf("NOTE: method count discrepancy (%d != %d + %d)\n",
+ methodCount, pClassData->header.directMethodsSize,
+ pClassData->header.virtualMethodsSize);
+ /* this is bad, but keep going anyway */
+ }
+
+ printf(" direct methods: %d\n",
+ pClassData->header.directMethodsSize);
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
+ }
+
+ printf(" virtual methods: %d\n",
+ pClassData->header.virtualMethodsSize);
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
+ }
+
+ free(pClassData);
+ }
+}
+
+/*
+ * Dump the requested sections of the file.
+ */
+void processDexFile(const char* fileName, DexFile* pDexFile)
+{
+ char* package = NULL;
+ int i;
+
+ if (gOptions.verbose) {
+ printf("Opened '%s', DEX version '%.3s'\n", fileName,
+ pDexFile->pHeader->magic +4);
+ }
+
+ if (gOptions.dumpRegisterMaps) {
+ dumpRegisterMaps(pDexFile);
+ return;
+ }
+
+ if (gOptions.showFileHeaders) {
+ dumpFileHeader(pDexFile);
+ dumpOptDirectory(pDexFile);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("<api>\n");
+
+ for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+ if (gOptions.showSectionHeaders)
+ dumpClassDef(pDexFile, i);
+
+ dumpClass(pDexFile, i, &package);
+ }
+
+ /* free the last one allocated */
+ if (package != NULL) {
+ printf("</package>\n");
+ free(package);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("</api>\n");
+}
+
+
+/*
+ * Process one file.
+ */
+int process(const char* fileName)
+{
+ DexFile* pDexFile = NULL;
+ MemMapping map;
+ bool mapped = false;
+ int result = -1;
+
+ if (gOptions.verbose)
+ printf("Processing '%s'...\n", fileName);
+
+ if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0) {
+ return result;
+ }
+ mapped = true;
+
+ int flags = kDexParseVerifyChecksum;
+ if (gOptions.ignoreBadChecksum)
+ flags |= kDexParseContinueOnError;
+
+ pDexFile = dexFileParse((u1*)map.addr, map.length, flags);
+ if (pDexFile == NULL) {
+ fprintf(stderr, "ERROR: DEX parse failed\n");
+ goto bail;
+ }
+
+ if (gOptions.checksumOnly) {
+ printf("Checksum verified\n");
+ } else {
+ processDexFile(fileName, pDexFile);
+ }
+
+ result = 0;
+
+bail:
+ if (mapped)
+ sysReleaseShmem(&map);
+ if (pDexFile != NULL)
+ dexFileFree(pDexFile);
+ return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr,
+ "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
+ gProgName);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -c : verify checksum and exit\n");
+ fprintf(stderr, " -d : disassemble code sections\n");
+ fprintf(stderr, " -f : display summary information from file header\n");
+ fprintf(stderr, " -h : display file header details\n");
+ fprintf(stderr, " -i : ignore checksum failures\n");
+ fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
+ fprintf(stderr, " -m : dump register maps (and nothing else)\n");
+ fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
+}
+
+/*
+ * Parse args.
+ *
+ * I'm not using getopt_long() because we may not have it in libc.
+ */
+int main(int argc, char* const argv[])
+{
+ bool wantUsage = false;
+ int ic;
+
+ memset(&gOptions, 0, sizeof(gOptions));
+ gOptions.verbose = true;
+
+ while (1) {
+ ic = getopt(argc, argv, "cdfhil:mt:");
+ if (ic < 0)
+ break;
+
+ switch (ic) {
+ case 'c': // verify the checksum then exit
+ gOptions.checksumOnly = true;
+ break;
+ case 'd': // disassemble Dalvik instructions
+ gOptions.disassemble = true;
+ break;
+ case 'f': // dump outer file header
+ gOptions.showFileHeaders = true;
+ break;
+ case 'h': // dump section headers, i.e. all meta-data
+ gOptions.showSectionHeaders = true;
+ break;
+ case 'i': // continue even if checksum is bad
+ gOptions.ignoreBadChecksum = true;
+ break;
+ case 'l': // layout
+ if (strcmp(optarg, "plain") == 0) {
+ gOptions.outputFormat = OUTPUT_PLAIN;
+ } else if (strcmp(optarg, "xml") == 0) {
+ gOptions.outputFormat = OUTPUT_XML;
+ gOptions.verbose = false;
+ gOptions.exportsOnly = true;
+ } else {
+ wantUsage = true;
+ }
+ break;
+ case 'm': // dump register maps only
+ gOptions.dumpRegisterMaps = true;
+ break;
+ case 't': // temp file, used when opening compressed Jar
+ gOptions.tempFileName = optarg;
+ break;
+ default:
+ wantUsage = true;
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ wantUsage = true;
+ }
+
+ if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
+ fprintf(stderr, "Can't specify both -c and -i\n");
+ wantUsage = true;
+ }
+
+ if (wantUsage) {
+ usage();
+ return 2;
+ }
+
+ int result = 0;
+ while (optind < argc) {
+ result |= process(argv[optind++]);
+ }
+
+ return (result != 0);
+}
diff --git a/dexdump/NOTICE b/dexdump/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/dexdump/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/dexgen/Android.mk b/dexgen/Android.mk
new file mode 100644
index 0000000..59d623a
--- /dev/null
+++ b/dexgen/Android.mk
@@ -0,0 +1,26 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 4
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := dexgen
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/dexgen/README.txt b/dexgen/README.txt
new file mode 100644
index 0000000..a542f04
--- /dev/null
+++ b/dexgen/README.txt
@@ -0,0 +1,3 @@
+Home of dexgen, the dex code generator project. It provides API for
+creating dex classes in runtime which is needed e.g. for class mocking.
+This solution is based on the dx tool and uses its classes extensively.
diff --git a/dexgen/src/com/android/dexgen/dex/code/ArrayData.java b/dexgen/src/com/android/dexgen/dex/code/ArrayData.java
new file mode 100644
index 0000000..d89a93f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.*;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+
+import java.util.ArrayList;
+
+/**
+ * Pseudo-instruction which holds fill array data.
+ */
+public final class ArrayData extends VariableSizeInsn {
+ /**
+ * {@code non-null;} address representing the instruction that uses this
+ * instance
+ */
+ private final CodeAddress user;
+
+ /** {@code 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}).
+ *
+ * @param position {@code non-null;} source position
+ * @param user {@code non-null;} address representing the instruction that
+ * uses this instance
+ * @param values {@code 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 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/dexgen/src/com/android/dexgen/dex/code/BlockAddresses.java b/dexgen/src/com/android/dexgen/dex/code/BlockAddresses.java
new file mode 100644
index 0000000..fa8096e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.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 {
+ /** {@code non-null;} array containing addresses for the start of each basic
+ * block (indexed by basic block label) */
+ private final CodeAddress[] starts;
+
+ /** {@code non-null;} array containing addresses for the final instruction
+ * of each basic block (indexed by basic block label) */
+ private final CodeAddress[] lasts;
+
+ /** {@code 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 {@code 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 {@code non-null;} the block in question
+ * @return {@code 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 {@code non-null;} the label of the block in question
+ * @return {@code 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 {@code non-null;} the block in question
+ * @return {@code 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 {@code non-null;} the label of the block in question
+ * @return {@code 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 {@code non-null;} the block in question
+ * @return {@code 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 {@code non-null;} the label of the block in question
+ * @return {@code 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/dexgen/src/com/android/dexgen/dex/code/CatchBuilder.java b/dexgen/src/com/android/dexgen/dex/code/CatchBuilder.java
new file mode 100644
index 0000000..adb119a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/CatchBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.dexgen.dex.code;
+
+import com.android.dexgen.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Interface for the construction of {@link CatchTable} instances.
+ */
+public interface CatchBuilder {
+ /**
+ * Builds and returns the catch table for this instance.
+ *
+ * @return {@code non-null;} the constructed table
+ */
+ public CatchTable build();
+
+ /**
+ * 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();
+
+ /**
+ * Gets the set of catch types associated with this instance.
+ *
+ * @return {@code non-null;} the set of catch types
+ */
+ public HashSet<Type> getCatchTypes();
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/CatchHandlerList.java b/dexgen/src/com/android/dexgen/dex/code/CatchHandlerList.java
new file mode 100644
index 0000000..54200ed
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.FixedSizeList;
+import com.android.dexgen.util.Hex;
+
+/**
+ * Ordered list of (exception type, handler address) entries.
+ */
+public final class CatchHandlerList extends FixedSizeList
+ implements Comparable<CatchHandlerList> {
+ /** {@code non-null;} empty instance */
+ public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 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}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code 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 {@code non-null;} the prefix for every line
+ * @param header {@code non-null;} the header for the first line (after the
+ * first prefix)
+ * @return {@code 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} if this instance ends with a "catch-all"
+ * handler or {@code false} 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 {@code >= 0, < size();} which index
+ * @param exceptionType {@code non-null;} type of exception handled
+ * @param handler {@code >= 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 {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ 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> {
+ /** {@code non-null;} type of exception handled */
+ private final CstType exceptionType;
+
+ /** {@code >= 0;} exception handler address */
+ private final int handler;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param exceptionType {@code non-null;} type of exception handled
+ * @param handler {@code >= 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 {@code non-null;} the exception type
+ */
+ public CstType getExceptionType() {
+ return exceptionType;
+ }
+
+ /**
+ * Gets the handler address.
+ *
+ * @return {@code >= 0;} the handler address
+ */
+ public int getHandler() {
+ return handler;
+ }
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/CatchTable.java b/dexgen/src/com/android/dexgen/dex/code/CatchTable.java
new file mode 100644
index 0000000..4de0da0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.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> {
+ /** {@code non-null;} empty instance */
+ public static final CatchTable EMPTY = new CatchTable(0);
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 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}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ 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> {
+ /** {@code >= 0;} start address */
+ private final int start;
+
+ /** {@code > start;} end address (exclusive) */
+ private final int end;
+
+ /** {@code non-null;} list of catch handlers */
+ private final CatchHandlerList handlers;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start {@code >= 0;} start address
+ * @param end {@code > start;} end address (exclusive)
+ * @param handlers {@code 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 {@code >= 0;} the start address
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end address (exclusive).
+ *
+ * @return {@code > start;} the end address (exclusive)
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the handlers.
+ *
+ * @return {@code non-null;} the handlers
+ */
+ public CatchHandlerList getHandlers() {
+ return handlers;
+ }
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/CodeAddress.java b/dexgen/src/com/android/dexgen/dex/code/CodeAddress.java
new file mode 100644
index 0000000..b9600ee
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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}).
+ *
+ * @param position {@code 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/dexgen/src/com/android/dexgen/dex/code/CstInsn.java b/dexgen/src/com/android/dexgen/dex/code/CstInsn.java
new file mode 100644
index 0000000..901266b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.Constant;
+
+/**
+ * Instruction which has a single constant argument in addition
+ * to all the normal instruction information.
+ */
+public final class CstInsn extends FixedSizeInsn {
+ /** {@code non-null;} the constant argument for this instruction */
+ private final Constant constant;
+
+ /**
+ * {@code >= -1;} the constant pool index for {@link #constant}, or
+ * {@code -1} if not yet set
+ */
+ private int index;
+
+ /**
+ * {@code >= -1;} the constant pool index for the class reference in
+ * {@link #constant} if any, or {@code -1} if not yet set
+ */
+ private int classIndex;
+
+ /**
+ * Constructs an instance. The output address of this instance is
+ * initially unknown ({@code -1}) as is the constant pool index.
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ * @param constant {@code 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 {@code 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 {@code >= 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} 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 {@code >= 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 {@code >= 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} 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 {@code >= 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/dexgen/src/com/android/dexgen/dex/code/DalvCode.java b/dexgen/src/com/android/dexgen/dex/code/DalvCode.java
new file mode 100644
index 0000000..2df49ed
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Container for all the pieces of a concrete method. Each instance
+ * corresponds to a {@code code} structure in a {@code .dex} file.
+ */
+public final class DalvCode {
+ /**
+ * how much position info to preserve; one of the static
+ * constants in {@link PositionList}
+ */
+ private final int positionInfo;
+
+ /**
+ * {@code null-ok;} the instruction list, ready for final processing;
+ * nulled out in {@link #finishProcessingIfNecessary}
+ */
+ private OutputFinisher unprocessedInsns;
+
+ /**
+ * {@code non-null;} unprocessed catch table;
+ * nulled out in {@link #finishProcessingIfNecessary}
+ */
+ private CatchBuilder unprocessedCatches;
+
+ /**
+ * {@code null-ok;} catch table; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private CatchTable catches;
+
+ /**
+ * {@code null-ok;} source positions list; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private PositionList positions;
+
+ /**
+ * {@code null-ok;} local variable list; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private LocalList locals;
+
+ /**
+ * {@code 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 {@code non-null;} the instruction list, ready
+ * for final processing
+ * @param unprocessedCatches {@code 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 {@code 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} 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} 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 {@code 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 {@code non-null;} the set of constants
+ */
+ public HashSet<Constant> getInsnConstants() {
+ return unprocessedInsns.getAllConstants();
+ }
+
+ /**
+ * Gets the list of instructions.
+ *
+ * @return {@code non-null;} the instruction list
+ */
+ public DalvInsnList getInsns() {
+ finishProcessingIfNecessary();
+ return insns;
+ }
+
+ /**
+ * Gets the catch (exception handler) table.
+ *
+ * @return {@code non-null;} the catch table
+ */
+ public CatchTable getCatches() {
+ finishProcessingIfNecessary();
+ return catches;
+ }
+
+ /**
+ * Gets the source positions list.
+ *
+ * @return {@code non-null;} the source positions list
+ */
+ public PositionList getPositions() {
+ finishProcessingIfNecessary();
+ return positions;
+ }
+
+ /**
+ * Gets the source positions list.
+ *
+ * @return {@code 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 {@code non-null;} the constant
+ * @return {@code >= -1;} the index or {@code -1} if the constant
+ * shouldn't actually be reified with an index
+ */
+ public int getIndex(Constant cst);
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/DalvInsn.java b/dexgen/src/com/android/dexgen/dex/code/DalvInsn.java
new file mode 100644
index 0000000..95b5feb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.util.TwoColumnOutput;
+
+/**
+ * Base class for Dalvik instructions.
+ */
+public abstract class DalvInsn {
+ /**
+ * the actual output address of this instance, if known, or
+ * {@code -1} if not
+ */
+ private int address;
+
+ /** the opcode; one of the constants from {@link Dops} */
+ private final Dop opcode;
+
+ /** {@code non-null;} source position */
+ private final SourcePosition position;
+
+ /** {@code non-null;} list of register arguments */
+ private final RegisterSpecList registers;
+
+ /**
+ * Makes a move instruction, appropriate and ideal for the given arguments.
+ *
+ * @param position {@code non-null;} source position information
+ * @param dest {@code non-null;} destination register
+ * @param src {@code non-null;} source register
+ * @return {@code 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}).
+ *
+ * <p><b>Note:</b> In the unlikely event that an instruction takes
+ * absolutely no registers (e.g., a {@code nop} 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 {@code non-null;} source position
+ * @param registers {@code 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} if it has not yet been set.
+ *
+ * @see #setAddress
+ *
+ * @return {@code >= 0;} the output address
+ */
+ public final int getAddress() {
+ if (address < 0) {
+ throw new RuntimeException("address not yet known");
+ }
+
+ return address;
+ }
+
+ /**
+ * Gets the opcode.
+ *
+ * @return {@code non-null;} the opcode
+ */
+ public final Dop getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Gets the source position.
+ *
+ * @return {@code non-null;} the source position
+ */
+ public final SourcePosition getPosition() {
+ return position;
+ }
+
+ /**
+ * Gets the register list for this instruction.
+ *
+ * @return {@code 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()}.
+ *
+ * @return {@code true} 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 {@code >= 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 {@code 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 {@code 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} (storing
+ * the result, if any, in register {@code 0} 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 {@code 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 {@code 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}
+ * if this instance should not appear in a listing.
+ *
+ * @param prefix {@code non-null;} prefix before the address; each follow-on
+ * line will be indented to match as well
+ * @param width {@code >= 0;} the width of the output or {@code 0} for
+ * unlimited width
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code null-ok;} the string form or {@code null} 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 {@code >= 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 {@code >= 0;} the next address
+ */
+ public final int getNextAddress() {
+ return getAddress() + codeSize();
+ }
+
+ /**
+ * Gets the size of this instruction, in 16-bit code units.
+ *
+ * @return {@code >= 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 {@code 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 {@code non-null;} the new opcode
+ * @return {@code 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 {@code 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 {@code non-null;} new register list
+ * @return {@code 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 {@code null-ok;} the string version of any arguments or
+ * {@code null} 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} if this instance should
+ * not appear in a listing.
+ *
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code null-ok;} the listing string
+ */
+ protected abstract String listingString0(boolean noteIndices);
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/DalvInsnList.java b/dexgen/src/com/android/dexgen/dex/code/DalvInsnList.java
new file mode 100644
index 0000000..15f82c1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstBaseMethodRef;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ExceptionWithContext;
+import com.android.dexgen.util.FixedSizeList;
+import com.android.dexgen.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 {@code non-null;} the list to use for elements
+ * @param regCount count, in register-units, of the number of registers
+ * this code block requires.
+ * @return {@code 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}.
+ *
+ * @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}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public DalvInsn get(int n) {
+ return (DalvInsn) get0(n);
+ }
+
+ /**
+ * Sets the instruction at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param insn {@code non-null;} the instruction to set at {@code n}
+ */
+ 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 {@code >= 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 {@code 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 {@code >= 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 {@code >= 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 {@code non-null;} where to dump
+ * @param prefix {@code 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 {@code non-null;} where to dump
+ * @param prefix {@code 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/dexgen/src/com/android/dexgen/dex/code/DalvOps.java b/dexgen/src/com/android/dexgen/dex/code/DalvOps.java
new file mode 100644
index 0000000..1d051ea
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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/dexgen/src/com/android/dexgen/dex/code/Dop.java b/dexgen/src/com/android/dexgen/dex/code/Dop.java
new file mode 100644
index 0000000..dc788f1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.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;
+
+ /** {@code non-null;} the instruction format */
+ private final InsnFormat format;
+
+ /** whether this opcode uses a result register */
+ private final boolean hasResult;
+
+ /** {@code non-null;} the name */
+ private final String name;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode
+ * value itself
+ * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ * @param format {@code non-null;} the instruction format
+ * @param hasResult whether the opcode has a result register; if so it
+ * is always the first register
+ * @param name {@code 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 {@code 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 {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ */
+ public int getFamily() {
+ return family;
+ }
+
+ /**
+ * Gets the instruction format.
+ *
+ * @return {@code non-null;} the instruction format
+ */
+ public InsnFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns whether this opcode uses a result register.
+ *
+ * @return {@code true} iff this opcode uses a result register
+ */
+ public boolean hasResult() {
+ return hasResult;
+ }
+
+ /**
+ * Gets the opcode name.
+ *
+ * @return {@code 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 {@code 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/dexgen/src/com/android/dexgen/dex/code/Dops.java b/dexgen/src/com/android/dexgen/dex/code/Dops.java
new file mode 100644
index 0000000..afd21e3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.dex.code.form.Form10t;
+import com.android.dexgen.dex.code.form.Form10x;
+import com.android.dexgen.dex.code.form.Form11n;
+import com.android.dexgen.dex.code.form.Form11x;
+import com.android.dexgen.dex.code.form.Form12x;
+import com.android.dexgen.dex.code.form.Form20t;
+import com.android.dexgen.dex.code.form.Form21c;
+import com.android.dexgen.dex.code.form.Form21h;
+import com.android.dexgen.dex.code.form.Form21s;
+import com.android.dexgen.dex.code.form.Form21t;
+import com.android.dexgen.dex.code.form.Form22b;
+import com.android.dexgen.dex.code.form.Form22c;
+import com.android.dexgen.dex.code.form.Form22s;
+import com.android.dexgen.dex.code.form.Form22t;
+import com.android.dexgen.dex.code.form.Form22x;
+import com.android.dexgen.dex.code.form.Form23x;
+import com.android.dexgen.dex.code.form.Form30t;
+import com.android.dexgen.dex.code.form.Form31c;
+import com.android.dexgen.dex.code.form.Form31i;
+import com.android.dexgen.dex.code.form.Form31t;
+import com.android.dexgen.dex.code.form.Form32x;
+import com.android.dexgen.dex.code.form.Form35c;
+import com.android.dexgen.dex.code.form.Form3rc;
+import com.android.dexgen.dex.code.form.Form51l;
+import com.android.dexgen.dex.code.form.SpecialFormat;
+
+/**
+ * Standard instances of {@link Dop} and utility methods for getting
+ * them.
+ */
+public final class Dops {
+ /** {@code 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 {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
+ * @return {@code 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 {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ * @param format {@code non-null;} the opcode's instruction format
+ * @return {@code null-ok;} the corresponding opcode, or {@code null} 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 {@code non-null;} the opcode
+ */
+ private static void set(Dop opcode) {
+ int idx = opcode.getOpcode() - DalvOps.MIN_VALUE;
+ DOPS[idx] = opcode;
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.java
new file mode 100644
index 0000000..28d8986
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/FixedSizeInsn.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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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 — but not all — instructions.
+ */
+public abstract class FixedSizeInsn extends DalvInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * <p><b>Note:</b> In the unlikely event that an instruction takes
+ * absolutely no registers (e.g., a {@code nop} 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 {@code non-null;} source position
+ * @param registers {@code 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/dexgen/src/com/android/dexgen/dex/code/HighRegisterPrefix.java b/dexgen/src/com/android/dexgen/dex/code/HighRegisterPrefix.java
new file mode 100644
index 0000000..08794ff
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Combination instruction which turns into a variable number of
+ * {@code move*} instructions to move a set of registers into
+ * registers starting at {@code 0} 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 {
+ /** {@code null-ok;} cached instructions, if constructed */
+ private SimpleInsn[] insns;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param registers {@code 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 {@code non-null;} the source register spec
+ * @param destIndex {@code >= 0;} the destination register index
+ * @return {@code 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/dexgen/src/com/android/dexgen/dex/code/InsnFormat.java b/dexgen/src/com/android/dexgen/dex/code/InsnFormat.java
new file mode 100644
index 0000000..bd5b60d
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.cst.CstKnownNull;
+import com.android.dexgen.rop.cst.CstLiteral64;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.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 {@code non-null;} the instruction
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code 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 ""}, not
+ * {@code null}.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param insn {@code non-null;} the instruction
+ * @return {@code 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 ""}, not
+ * {@code null}.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param insn {@code non-null;} the instruction
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code 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 {@code >= 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 {@code non-null;} the instruction to check
+ * @return {@code true} iff the instruction's arguments are
+ * appropriate for this instance, or {@code false} 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}
+ * for formats that don't include a branch offset.
+ *
+ * <p>The default implementation of this method always returns
+ * {@code false}. Subclasses must override this method if they
+ * include branch offsets.</p>
+ *
+ * @param insn {@code non-null;} the instruction to check
+ * @return {@code true} iff the instruction's branch offset is
+ * appropriate for this instance, or {@code false} 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 {@code null-ok;} the next format to try, or {@code null} 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 {@code non-null;} the output destination to write to
+ * @param insn {@code non-null;} the instruction to write
+ */
+ public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
+
+ /**
+ * Helper method to return a register list string.
+ *
+ * @param list {@code non-null;} the list of registers
+ * @return {@code 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 {@code 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}
+ * @return {@code 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 {@code non-null;} the instruction in question
+ * @return {@code 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 {@code non-null;} the instruction in question
+ * @return {@code 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 {@code non-null;} a constant-bearing instruction
+ * @return {@code 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 {@code non-null;} a constant-bearing instruction
+ * @return {@code 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} 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} 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} 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} 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} 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} 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} 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 {@code non-null;} the instruction
+ * @return {@code >= 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 {@code non-null;} the instruction containing the opcode
+ * @param arg {@code 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 {@code 0..255;} low byte
+ * @param high {@code 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 {@code 0..15;} low nibble
+ * @param n1 {@code 0..15;} medium-low nibble
+ * @param n2 {@code 0..15;} medium-high nibble
+ * @param n3 {@code 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 {@code 0..15;} low nibble
+ * @param high {@code 0..15;} high nibble
+ * @return {@code 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 {@code 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 {@code 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 {@code 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 {@code 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 {@code 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 {@code 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/dexgen/src/com/android/dexgen/dex/code/LocalEnd.java b/dexgen/src/com/android/dexgen/dex/code/LocalEnd.java
new file mode 100644
index 0000000..130b08b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/LocalEnd.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to explicitly end the mapping of a
+ * register to a named local variable. That is, an instance of this
+ * class in an instruction stream indicates that starting with the
+ * subsequent instruction, the indicated variable is no longer valid.
+ */
+public final class LocalEnd extends ZeroSizeInsn {
+ /**
+ * {@code non-null;} register spec representing the local variable ended
+ * by this instance. <b>Note:</b> Technically, only the register
+ * number needs to be recorded here as the rest of the information
+ * is implicit in the ambient local variable state, but other code
+ * will check the other info for consistency.
+ */
+ private final RegisterSpec local;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param local {@code non-null;} register spec representing the local
+ * variable introduced by this instance
+ */
+ public LocalEnd(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 LocalEnd(getPosition(), local.withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new LocalEnd(getPosition(), local);
+ }
+
+ /**
+ * Gets the register spec representing the local variable ended
+ * by this instance.
+ *
+ * @return {@code 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-end " + LocalStart.localString(local);
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/LocalList.java b/dexgen/src/com/android/dexgen/dex/code/LocalList.java
new file mode 100644
index 0000000..c1c3921
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/LocalList.java
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.FixedSizeList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * 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 {
+ /** {@code non-null;} empty instance */
+ public static final LocalList EMPTY = new LocalList(0);
+
+ /** whether to run the self-check code */
+ private static final boolean DEBUG = false;
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 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}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ */
+ public void debugPrint(PrintStream out, String prefix) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ out.print(prefix);
+ out.println(get(i));
+ }
+ }
+
+ /**
+ * Disposition of a local entry.
+ */
+ public static enum Disposition {
+ /** local started (introduced) */
+ START,
+
+ /** local ended without being replaced */
+ END_SIMPLY,
+
+ /** local ended because it was directly replaced */
+ END_REPLACED,
+
+ /** local ended because it was moved to a different register */
+ END_MOVED,
+
+ /**
+ * local ended because the previous local clobbered this one
+ * (because it is category-2)
+ */
+ END_CLOBBERED_BY_PREV,
+
+ /**
+ * local ended because the next local clobbered this one
+ * (because this one is a category-2)
+ */
+ END_CLOBBERED_BY_NEXT;
+ }
+
+ /**
+ * Entry in a local list.
+ */
+ public static class Entry implements Comparable<Entry> {
+ /** {@code >= 0;} address */
+ private final int address;
+
+ /** {@code non-null;} disposition of the local */
+ private final Disposition disposition;
+
+ /** {@code non-null;} register spec representing the variable */
+ private final RegisterSpec spec;
+
+ /** {@code non-null;} variable type (derived from {@code spec}) */
+ private final CstType type;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param address {@code >= 0;} address
+ * @param disposition {@code non-null;} disposition of the local
+ * @param spec {@code non-null;} register spec representing
+ * the variable
+ */
+ public Entry(int address, Disposition disposition, RegisterSpec spec) {
+ if (address < 0) {
+ throw new IllegalArgumentException("address < 0");
+ }
+
+ if (disposition == null) {
+ throw new NullPointerException("disposition == null");
+ }
+
+ try {
+ if (spec.getLocalItem() == null) {
+ throw new NullPointerException(
+ "spec.getLocalItem() == null");
+ }
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("spec == null");
+ }
+
+ this.address = address;
+ this.disposition = disposition;
+ this.spec = spec;
+ this.type = CstType.intern(spec.getType());
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return Integer.toHexString(address) + " " + disposition + " " +
+ spec;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (!(other instanceof Entry)) {
+ return false;
+ }
+
+ return (compareTo((Entry) other) == 0);
+ }
+
+ /**
+ * Compares by (in priority order) address, end then start
+ * disposition (variants of end are all consistered
+ * equivalent), and spec.
+ *
+ * @param other {@code non-null;} entry to compare to
+ * @return {@code -1..1;} standard result of comparison
+ */
+ public int compareTo(Entry other) {
+ if (address < other.address) {
+ return -1;
+ } else if (address > other.address) {
+ return 1;
+ }
+
+ boolean thisIsStart = isStart();
+ boolean otherIsStart = other.isStart();
+
+ if (thisIsStart != otherIsStart) {
+ return thisIsStart ? 1 : -1;
+ }
+
+ return spec.compareTo(other.spec);
+ }
+
+ /**
+ * Gets the address.
+ *
+ * @return {@code >= 0;} the address
+ */
+ public int getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the disposition.
+ *
+ * @return {@code non-null;} the disposition
+ */
+ public Disposition getDisposition() {
+ return disposition;
+ }
+
+ /**
+ * Gets whether this is a local start. This is just shorthand for
+ * {@code getDisposition() == Disposition.START}.
+ *
+ * @return {@code true} iff this is a start
+ */
+ public boolean isStart() {
+ return disposition == Disposition.START;
+ }
+
+ /**
+ * Gets the variable name.
+ *
+ * @return {@code null-ok;} the variable name
+ */
+ public CstUtf8 getName() {
+ return spec.getLocalItem().getName();
+ }
+
+ /**
+ * Gets the variable signature.
+ *
+ * @return {@code null-ok;} the variable signature
+ */
+ public CstUtf8 getSignature() {
+ return spec.getLocalItem().getSignature();
+ }
+
+ /**
+ * Gets the variable's type.
+ *
+ * @return {@code non-null;} the type
+ */
+ public CstType getType() {
+ return type;
+ }
+
+ /**
+ * Gets the number of the register holding the variable.
+ *
+ * @return {@code >= 0;} the number of the register holding
+ * the variable
+ */
+ public int getRegister() {
+ return spec.getReg();
+ }
+
+ /**
+ * Gets the RegisterSpec of the register holding the variable.
+ *
+ * @return {@code non-null;} RegisterSpec of the holding register.
+ */
+ public RegisterSpec getRegisterSpec() {
+ return spec;
+ }
+
+ /**
+ * Returns whether or not this instance matches the given spec.
+ *
+ * @param otherSpec {@code non-null;} the spec in question
+ * @return {@code true} iff this instance matches
+ * {@code spec}
+ */
+ public boolean matches(RegisterSpec otherSpec) {
+ return spec.equalsUsingSimpleType(otherSpec);
+ }
+
+ /**
+ * Returns whether or not this instance matches the spec in
+ * the given instance.
+ *
+ * @param other {@code non-null;} another entry
+ * @return {@code true} iff this instance's spec matches
+ * {@code other}
+ */
+ public boolean matches(Entry other) {
+ return matches(other.spec);
+ }
+
+ /**
+ * Returns an instance just like this one but with the disposition
+ * set as given.
+ *
+ * @param disposition {@code non-null;} the new disposition
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Entry withDisposition(Disposition disposition) {
+ if (disposition == this.disposition) {
+ return this;
+ }
+
+ return new Entry(address, disposition, spec);
+ }
+ }
+
+ /**
+ * Constructs an instance for the given method, based on the given
+ * block order and intermediate local information.
+ *
+ * @param insns {@code non-null;} instructions to convert
+ * @return {@code non-null;} the constructed list
+ */
+ public static LocalList make(DalvInsnList insns) {
+ int sz = insns.size();
+
+ /*
+ * Go through the insn list, looking for all the local
+ * variable pseudoinstructions, splitting out LocalSnapshots
+ * into separate per-variable starts, adding explicit ends
+ * wherever a variable is replaced or moved, and collecting
+ * these and all the other local variable "activity"
+ * together into an output list (without the other insns).
+ *
+ * Note: As of this writing, this method won't be handed any
+ * insn lists that contain local ends, but I (danfuzz) expect
+ * that to change at some point, when we start feeding that
+ * info explicitly into the rop layer rather than only trying
+ * to infer it. So, given that expectation, this code is
+ * written to deal with them.
+ */
+
+ MakeState state = new MakeState(sz);
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = insns.get(i);
+
+ if (insn instanceof LocalSnapshot) {
+ RegisterSpecSet snapshot =
+ ((LocalSnapshot) insn).getLocals();
+ state.snapshot(insn.getAddress(), snapshot);
+ } else if (insn instanceof LocalStart) {
+ RegisterSpec local = ((LocalStart) insn).getLocal();
+ state.startLocal(insn.getAddress(), local);
+ } else if (insn instanceof LocalEnd) {
+ RegisterSpec local = ((LocalEnd) insn).getLocal();
+ state.endLocal(insn.getAddress(), local);
+ }
+ }
+
+ LocalList result = state.finish();
+
+ if (DEBUG) {
+ debugVerify(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Debugging helper that verifies the constraint that a list doesn't
+ * contain any redundant local starts and that local ends that are
+ * due to replacements are properly annotated.
+ */
+ private static void debugVerify(LocalList locals) {
+ try {
+ debugVerify0(locals);
+ } catch (RuntimeException ex) {
+ int sz = locals.size();
+ for (int i = 0; i < sz; i++) {
+ System.err.println(locals.get(i));
+ }
+ throw ex;
+ }
+
+ }
+
+ /**
+ * Helper for {@link #debugVerify} which does most of the work.
+ */
+ private static void debugVerify0(LocalList locals) {
+ int sz = locals.size();
+ Entry[] active = new Entry[65536];
+
+ for (int i = 0; i < sz; i++) {
+ Entry e = locals.get(i);
+ int reg = e.getRegister();
+
+ if (e.isStart()) {
+ Entry already = active[reg];
+
+ if ((already != null) && e.matches(already)) {
+ throw new RuntimeException("redundant start at " +
+ Integer.toHexString(e.getAddress()) + ": got " +
+ e + "; had " + already);
+ }
+
+ active[reg] = e;
+ } else {
+ if (active[reg] == null) {
+ throw new RuntimeException("redundant end at " +
+ Integer.toHexString(e.getAddress()));
+ }
+
+ int addr = e.getAddress();
+ boolean foundStart = false;
+
+ for (int j = i + 1; j < sz; j++) {
+ Entry test = locals.get(j);
+ if (test.getAddress() != addr) {
+ break;
+ }
+ if (test.getRegisterSpec().getReg() == reg) {
+ if (test.isStart()) {
+ if (e.getDisposition()
+ != Disposition.END_REPLACED) {
+ throw new RuntimeException(
+ "improperly marked end at " +
+ Integer.toHexString(addr));
+ }
+ foundStart = true;
+ } else {
+ throw new RuntimeException(
+ "redundant end at " +
+ Integer.toHexString(addr));
+ }
+ }
+ }
+
+ if (!foundStart &&
+ (e.getDisposition() == Disposition.END_REPLACED)) {
+ throw new RuntimeException(
+ "improper end replacement claim at " +
+ Integer.toHexString(addr));
+ }
+
+ active[reg] = null;
+ }
+ }
+ }
+
+ /**
+ * Intermediate state when constructing a local list.
+ */
+ public static class MakeState {
+ /** {@code non-null;} result being collected */
+ private final ArrayList<Entry> result;
+
+ /**
+ * {@code >= 0;} running count of nulled result entries, to help with
+ * sizing the final list
+ */
+ private int nullResultCount;
+
+ /** {@code null-ok;} current register mappings */
+ private RegisterSpecSet regs;
+
+ /** {@code null-ok;} result indices where local ends are stored */
+ private int[] endIndices;
+
+ /** {@code >= 0;} last address seen */
+ private int lastAddress;
+
+ /**
+ * Constructs an instance.
+ */
+ public MakeState(int initialSize) {
+ result = new ArrayList<Entry>(initialSize);
+ nullResultCount = 0;
+ regs = null;
+ endIndices = null;
+ lastAddress = 0;
+ }
+
+ /**
+ * Checks the address and other vitals as a prerequisite to
+ * further processing.
+ *
+ * @param address {@code >= 0;} address about to be processed
+ * @param reg {@code >= 0;} register number about to be processed
+ */
+ private void aboutToProcess(int address, int reg) {
+ boolean first = (endIndices == null);
+
+ if ((address == lastAddress) && !first) {
+ return;
+ }
+
+ if (address < lastAddress) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ if (first || (reg >= endIndices.length)) {
+ /*
+ * This is the first allocation of the state set and
+ * index array, or we need to grow. (The latter doesn't
+ * happen much; in fact, we have only ever observed
+ * it happening in test cases, never in "real" code.)
+ */
+ int newSz = reg + 1;
+ RegisterSpecSet newRegs = new RegisterSpecSet(newSz);
+ int[] newEnds = new int[newSz];
+ Arrays.fill(newEnds, -1);
+
+ if (!first) {
+ newRegs.putAll(regs);
+ System.arraycopy(endIndices, 0, newEnds, 0,
+ endIndices.length);
+ }
+
+ regs = newRegs;
+ endIndices = newEnds;
+ }
+ }
+
+ /**
+ * Sets the local state at the given address to the given snapshot.
+ * The first call on this instance must be to this method, so that
+ * the register state can be properly sized.
+ *
+ * @param address {@code >= 0;} the address
+ * @param specs {@code non-null;} spec set representing the locals
+ */
+ public void snapshot(int address, RegisterSpecSet specs) {
+ if (DEBUG) {
+ System.err.printf("%04x snapshot %s\n", address, specs);
+ }
+
+ int sz = specs.getMaxSize();
+ aboutToProcess(address, sz - 1);
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec oldSpec = regs.get(i);
+ RegisterSpec newSpec = filterSpec(specs.get(i));
+
+ if (oldSpec == null) {
+ if (newSpec != null) {
+ startLocal(address, newSpec);
+ }
+ } else if (newSpec == null) {
+ endLocal(address, oldSpec);
+ } else if (! newSpec.equalsUsingSimpleType(oldSpec)) {
+ endLocal(address, oldSpec);
+ startLocal(address, newSpec);
+ }
+ }
+
+ if (DEBUG) {
+ System.err.printf("%04x snapshot done\n", address);
+ }
+ }
+
+ /**
+ * Starts a local at the given address.
+ *
+ * @param address {@code >= 0;} the address
+ * @param startedLocal {@code non-null;} spec representing the
+ * started local
+ */
+ public void startLocal(int address, RegisterSpec startedLocal) {
+ if (DEBUG) {
+ System.err.printf("%04x start %s\n", address, startedLocal);
+ }
+
+ int regNum = startedLocal.getReg();
+
+ startedLocal = filterSpec(startedLocal);
+ aboutToProcess(address, regNum);
+
+ RegisterSpec existingLocal = regs.get(regNum);
+
+ if (startedLocal.equalsUsingSimpleType(existingLocal)) {
+ // Silently ignore a redundant start.
+ return;
+ }
+
+ RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal);
+ if (movedLocal != null) {
+ /*
+ * The same variable was moved from one register to another.
+ * So add an end for its old location.
+ */
+ addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal);
+ }
+
+ int endAt = endIndices[regNum];
+
+ if (existingLocal != null) {
+ /*
+ * There is an existing (but non-matching) local.
+ * Add an explicit end for it.
+ */
+ add(address, Disposition.END_REPLACED, existingLocal);
+ } else if (endAt >= 0) {
+ /*
+ * Look for an end local for the same register at the
+ * same address. If found, then update it or delete
+ * it, depending on whether or not it represents the
+ * same variable as the one being started.
+ */
+ Entry endEntry = result.get(endAt);
+ if (endEntry.getAddress() == address) {
+ if (endEntry.matches(startedLocal)) {
+ /*
+ * There was already an end local for the same
+ * variable at the same address. This turns
+ * out to be superfluous, as we are starting
+ * up the exact same local. This situation can
+ * happen when a single local variable got
+ * somehow "split up" during intermediate
+ * processing. In any case, rather than represent
+ * the end-then-start, just remove the old end.
+ */
+ result.set(endAt, null);
+ nullResultCount++;
+ regs.put(startedLocal);
+ endIndices[regNum] = -1;
+ return;
+ } else {
+ /*
+ * There was a different variable ended at the
+ * same address. Update it to indicate that
+ * it was ended due to a replacement (rather than
+ * ending for no particular reason).
+ */
+ endEntry = endEntry.withDisposition(
+ Disposition.END_REPLACED);
+ result.set(endAt, endEntry);
+ }
+ }
+ }
+
+ /*
+ * The code above didn't find and remove an unnecessary
+ * local end, so we now have to add one or more entries to
+ * the output to capture the transition.
+ */
+
+ /*
+ * If the local just below (in the register set at reg-1)
+ * is of category-2, then it is ended by this new start.
+ */
+ if (regNum > 0) {
+ RegisterSpec justBelow = regs.get(regNum - 1);
+ if ((justBelow != null) && justBelow.isCategory2()) {
+ addOrUpdateEnd(address,
+ Disposition.END_CLOBBERED_BY_NEXT,
+ justBelow);
+ }
+ }
+
+ /*
+ * Similarly, if this local is category-2, then the local
+ * just above (if any) is ended by the start now being
+ * emitted.
+ */
+ if (startedLocal.isCategory2()) {
+ RegisterSpec justAbove = regs.get(regNum + 1);
+ if (justAbove != null) {
+ addOrUpdateEnd(address,
+ Disposition.END_CLOBBERED_BY_PREV,
+ justAbove);
+ }
+ }
+
+ /*
+ * TODO: Add an end for the same local in a different reg,
+ * if any (that is, if the local migrates from vX to vY,
+ * we should note that as a local end in vX).
+ */
+
+ add(address, Disposition.START, startedLocal);
+ }
+
+ /**
+ * Ends a local at the given address, using the disposition
+ * {@code END_SIMPLY}.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ */
+ public void endLocal(int address, RegisterSpec endedLocal) {
+ endLocal(address, endedLocal, Disposition.END_SIMPLY);
+ }
+
+ /**
+ * Ends a local at the given address.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ * @param disposition reason for the end
+ */
+ public void endLocal(int address, RegisterSpec endedLocal,
+ Disposition disposition) {
+ if (DEBUG) {
+ System.err.printf("%04x end %s\n", address, endedLocal);
+ }
+
+ int regNum = endedLocal.getReg();
+
+ endedLocal = filterSpec(endedLocal);
+ aboutToProcess(address, regNum);
+
+ int endAt = endIndices[regNum];
+
+ if (endAt >= 0) {
+ /*
+ * The local in the given register is already ended.
+ * Silently return without adding anything to the result.
+ */
+ return;
+ }
+
+ // Check for start and end at the same address.
+ if (checkForEmptyRange(address, endedLocal)) {
+ return;
+ }
+
+ add(address, disposition, endedLocal);
+ }
+
+ /**
+ * Helper for {@link #endLocal}, which handles the cases where
+ * and end local is issued at the same address as a start local
+ * for the same register. If this case is found, then this
+ * method will remove the start (as the local was never actually
+ * active), update the {@link #endIndices} to be accurate, and
+ * if needed update the newly-active end to reflect an altered
+ * disposition.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ * @return {@code true} iff this method found the case in question
+ * and adjusted things accordingly
+ */
+ private boolean checkForEmptyRange(int address,
+ RegisterSpec endedLocal) {
+ int at = result.size() - 1;
+ Entry entry;
+
+ // Look for a previous entry at the same address.
+ for (/*at*/; at >= 0; at--) {
+ entry = result.get(at);
+
+ if (entry == null) {
+ continue;
+ }
+
+ if (entry.getAddress() != address) {
+ // We didn't find any match at the same address.
+ return false;
+ }
+
+ if (entry.matches(endedLocal)) {
+ break;
+ }
+ }
+
+ /*
+ * In fact, we found that the endedLocal had started at the
+ * same address, so do all the requisite cleanup.
+ */
+
+ regs.remove(endedLocal);
+ result.set(at, null);
+ nullResultCount++;
+
+ int regNum = endedLocal.getReg();
+ boolean found = false;
+ entry = null;
+
+ // Now look back further to update where the register ended.
+ for (at--; at >= 0; at--) {
+ entry = result.get(at);
+
+ if (entry == null) {
+ continue;
+ }
+
+ if (entry.getRegisterSpec().getReg() == regNum) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ // We found an end for the same register.
+ endIndices[regNum] = at;
+
+ if (entry.getAddress() == address) {
+ /*
+ * It's still the same address, so update the
+ * disposition.
+ */
+ result.set(at,
+ entry.withDisposition(Disposition.END_SIMPLY));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Converts a given spec into the form acceptable for use in a
+ * local list. This, in particular, transforms the "known
+ * null" type into simply {@code Object}. This method needs to
+ * be called for any spec that is on its way into a locals
+ * list.
+ *
+ * <p>This isn't necessarily the cleanest way to achieve the
+ * goal of not representing known nulls in a locals list, but
+ * it gets the job done.</p>
+ *
+ * @param orig {@code null-ok;} the original spec
+ * @return {@code null-ok;} an appropriately modified spec, or the
+ * original if nothing needs to be done
+ */
+ private static RegisterSpec filterSpec(RegisterSpec orig) {
+ if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) {
+ return orig.withType(Type.OBJECT);
+ }
+
+ return orig;
+ }
+
+ /**
+ * Adds an entry to the result, updating the adjunct tables
+ * accordingly.
+ *
+ * @param address {@code >= 0;} the address
+ * @param disposition {@code non-null;} the disposition
+ * @param spec {@code non-null;} spec representing the local
+ */
+ private void add(int address, Disposition disposition,
+ RegisterSpec spec) {
+ int regNum = spec.getReg();
+
+ result.add(new Entry(address, disposition, spec));
+
+ if (disposition == Disposition.START) {
+ regs.put(spec);
+ endIndices[regNum] = -1;
+ } else {
+ regs.remove(spec);
+ endIndices[regNum] = result.size() - 1;
+ }
+ }
+
+ /**
+ * Adds or updates an end local (changing its disposition). If
+ * this would cause an empty range for a local, this instead
+ * removes the local entirely.
+ *
+ * @param address {@code >= 0;} the address
+ * @param disposition {@code non-null;} the disposition
+ * @param spec {@code non-null;} spec representing the local
+ */
+ private void addOrUpdateEnd(int address, Disposition disposition,
+ RegisterSpec spec) {
+ if (disposition == Disposition.START) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ int regNum = spec.getReg();
+ int endAt = endIndices[regNum];
+
+ if (endAt >= 0) {
+ // There is a previous end.
+ Entry endEntry = result.get(endAt);
+ if ((endEntry.getAddress() == address) &&
+ endEntry.getRegisterSpec().equals(spec)) {
+ /*
+ * The end is for the right address and variable, so
+ * update it.
+ */
+ result.set(endAt, endEntry.withDisposition(disposition));
+ regs.remove(spec); // TODO: Is this line superfluous?
+ return;
+ }
+ }
+
+ endLocal(address, spec, disposition);
+ }
+
+ /**
+ * Finishes processing altogether and gets the result.
+ *
+ * @return {@code non-null;} the result list
+ */
+ public LocalList finish() {
+ aboutToProcess(Integer.MAX_VALUE, 0);
+
+ int resultSz = result.size();
+ int finalSz = resultSz - nullResultCount;
+
+ if (finalSz == 0) {
+ return EMPTY;
+ }
+
+ /*
+ * Collect an array of only the non-null entries, and then
+ * sort it to get a consistent order for everything: Local
+ * ends and starts for a given address could come in any
+ * order, but we want ends before starts as well as
+ * registers in order (within ends or starts).
+ */
+
+ Entry[] resultArr = new Entry[finalSz];
+
+ if (resultSz == finalSz) {
+ result.toArray(resultArr);
+ } else {
+ int at = 0;
+ for (Entry e : result) {
+ if (e != null) {
+ resultArr[at++] = e;
+ }
+ }
+ }
+
+ Arrays.sort(resultArr);
+
+ LocalList resultList = new LocalList(finalSz);
+
+ for (int i = 0; i < finalSz; i++) {
+ resultList.set(i, resultArr[i]);
+ }
+
+ resultList.setImmutable();
+ return resultList;
+ }
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/LocalSnapshot.java b/dexgen/src/com/android/dexgen/dex/code/LocalSnapshot.java
new file mode 100644
index 0000000..81b78c9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.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 {
+ /** {@code 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}).
+ *
+ * @param position {@code non-null;} source position
+ * @param locals {@code 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 {@code 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(LocalStart.localString(spec));
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/LocalStart.java b/dexgen/src/com/android/dexgen/dex/code/LocalStart.java
new file mode 100644
index 0000000..ba426e8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/LocalStart.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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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 bound.
+ */
+public final class LocalStart extends ZeroSizeInsn {
+ /**
+ * {@code 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 {@code non-null;} the spec to convert
+ * @return {@code 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}).
+ *
+ * @param position {@code non-null;} source position
+ * @param local {@code non-null;} register spec representing the local
+ * variable introduced by this instance
+ */
+ public LocalStart(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 LocalStart(getPosition(), local.withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new LocalStart(getPosition(), local);
+ }
+
+ /**
+ * Gets the register spec representing the local variable introduced
+ * by this instance.
+ *
+ * @return {@code 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-start " + localString(local);
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/OddSpacer.java b/dexgen/src/com/android/dexgen/dex/code/OddSpacer.java
new file mode 100644
index 0000000..8e2bab8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction which either turns into a {@code nop} 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}).
+ *
+ * @param position {@code 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/dexgen/src/com/android/dexgen/dex/code/OutputCollector.java b/dexgen/src/com/android/dexgen/dex/code/OutputCollector.java
new file mode 100644
index 0000000..7f970eb
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import java.util.ArrayList;
+
+/**
+ * Destination for {@link DalvInsn} instances being output. This class
+ * receives and collects instructions in two pieces — a primary
+ * list and a suffix (generally consisting of adjunct data referred to
+ * by the primary list, such as switch case tables) — which it
+ * merges and emits back out in the form of a {@link DalvInsnList}
+ * instance.
+ */
+public final class OutputCollector {
+ /**
+ * {@code non-null;} the associated finisher (which holds the instruction
+ * list in-progress)
+ */
+ private final OutputFinisher finisher;
+
+ /**
+ * {@code null-ok;} suffix for the output, or {@code null} if the suffix
+ * has been appended to the main output (by {@link #appendSuffixToOutput})
+ */
+ private ArrayList<DalvInsn> suffix;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param initialCapacity {@code >= 0;} initial capacity of the output list
+ * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output
+ * suffix
+ * @param regCount {@code >= 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 {@code 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} is the most recently added instruction,
+ * {@code 1} is the instruction before that, etc.
+ * @param newTarget {@code 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 {@code 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 {@code 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/dexgen/src/com/android/dexgen/dex/code/OutputFinisher.java b/dexgen/src/com/android/dexgen/dex/code/OutputFinisher.java
new file mode 100644
index 0000000..98c7e4e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.LocalItem;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMemberRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.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 {
+ /**
+ * {@code >= 0;} register count for the method, not including any extra
+ * "reserved" registers needed to translate "difficult" instructions
+ */
+ private final int unreservedRegCount;
+
+ /** {@code 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;
+
+ /**
+ * {@code >= 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 {@code >= 0;} register count for the method
+ * @param initialCapacity {@code >= 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 {@code non-null;} instruction to scrutinize
+ * @return {@code true} 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 LocalStart) {
+ RegisterSpec spec = ((LocalStart) insn).getLocal();
+ if (hasLocalInfo(spec)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
+ * register spec.
+ *
+ * @param spec {@code non-null;} spec to scrutinize
+ * @return {@code true} 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 {@code 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 {@code non-null;} result set to add to
+ * @param insn {@code 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 LocalStart) {
+ RegisterSpec spec = ((LocalStart) insn).getLocal();
+ addConstants(result, spec);
+ }
+ }
+
+ /**
+ * Helper for {@link #getAllConstants} which adds all the info for
+ * a single {@code RegisterSpec}.
+ *
+ * @param result {@code non-null;} result set to add to
+ * @param spec {@code 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 {@code 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 {@code >= 0;} what index to insert at
+ * @param insn {@code 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 {@code 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} is the most recently added instruction,
+ * {@code 1} is the instruction before that, etc.
+ * @param newTarget {@code 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 {@code 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 {@code non-null;} the instruction
+ * @param callback {@code 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 {@code 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 {@code 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} array so
+ * as to avoid extra work when constructing the massaged
+ * instruction list.
+ *
+ * @param formats {@code 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} list to help avoid extra work in future
+ * register reservation passes.
+ *
+ * @param formats {@code non-null;} array of per-instruction format selections
+ * @return {@code >= 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}
+ * 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 {@code non-null;} the instruction in question
+ * @param format {@code null-ok;} the current guess as to the best instruction
+ * format to use; {@code null} means that no simple format fits
+ * @return {@code null-ok;} a possibly-different format, which is either a
+ * good fit or {@code null} 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 {@code 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 {@code non-null;} array of per-instruction format selections
+ * @return {@code 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} 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}
+ * 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/dexgen/src/com/android/dexgen/dex/code/PositionList.java b/dexgen/src/com/android/dexgen/dex/code/PositionList.java
new file mode 100644
index 0000000..8b52f26
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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 {
+ /** {@code 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 {@code non-null;} instructions to convert
+ * @param howMuch how much information should be included; one of the
+ * static constants defined by this class
+ * @return {@code 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}.
+ *
+ * @param size {@code >= 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}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /**
+ * Entry in a position list.
+ */
+ public static class Entry {
+ /** {@code >= 0;} address of this entry */
+ private final int address;
+
+ /** {@code non-null;} corresponding source position information */
+ private final SourcePosition position;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param address {@code >= 0;} address of this entry
+ * @param position {@code 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 {@code >= 0;} the address
+ */
+ public int getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the source position information.
+ *
+ * @return {@code non-null;} the position information
+ */
+ public SourcePosition getPosition() {
+ return position;
+ }
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/RopToDop.java b/dexgen/src/com/android/dexgen/dex/code/RopToDop.java
new file mode 100644
index 0000000..03d1de8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.RegOps;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.Rop;
+import com.android.dexgen.rop.code.Rops;
+import com.android.dexgen.rop.code.ThrowingCstInsn;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Translator from rop-level {@link Insn} instances to corresponding
+ * {@link Dop} instances.
+ */
+public final class RopToDop {
+ /** {@code 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 {@code 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/dexgen/src/com/android/dexgen/dex/code/RopTranslator.java b/dexgen/src/com/android/dexgen/dex/code/RopTranslator.java
new file mode 100644
index 0000000..ad05f2b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/RopTranslator.java
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.FillArrayDataInsn;
+import com.android.dexgen.rop.code.Insn;
+import com.android.dexgen.rop.code.LocalVariableInfo;
+import com.android.dexgen.rop.code.PlainCstInsn;
+import com.android.dexgen.rop.code.PlainInsn;
+import com.android.dexgen.rop.code.RegOps;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.RegisterSpecSet;
+import com.android.dexgen.rop.code.Rop;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.rop.code.SwitchInsn;
+import com.android.dexgen.rop.code.ThrowingCstInsn;
+import com.android.dexgen.rop.code.ThrowingInsn;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstInteger;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.Bits;
+import com.android.dexgen.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 {
+ /** {@code 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;
+
+ /** {@code null-ok;} local variable info to use */
+ private final LocalVariableInfo locals;
+
+ /** {@code non-null;} container for all the address objects for the method */
+ private final BlockAddresses addresses;
+
+ /** {@code non-null;} list of output instructions in-progress */
+ private final OutputCollector output;
+
+ /** {@code non-null;} visitor to use during translation */
+ private final TranslationVisitor translationVisitor;
+
+ /** {@code >= 0;} register count for the method */
+ private final int regCount;
+
+ /** {@code 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 {@code non-null;} the original method
+ * @param positionInfo how much position info to preserve; one of the
+ * static constants in {@link PositionList}
+ * @param locals {@code null-ok;} local variable information to use
+ * @param paramSize size, in register units, of all the parameters to
+ * this method
+ * @return {@code 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 {@code non-null;} the original method
+ * @param positionInfo how much position info to preserve; one of the
+ * static constants in {@link PositionList}
+ * @param locals {@code 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} 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 {@code non-null;} the result
+ */
+ private DalvCode translateAndGetResult() {
+ pickOrder();
+ outputInstructions();
+
+ StdCatchBuilder catches =
+ new StdCatchBuilder(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 {@code non-null;} the block to process and output
+ * @param nextLabel {@code >= -1;} the next block that will be processed, or
+ * {@code -1} 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 {@code non-null;} instruction in question
+ * @return {@code 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 {@code non-null;} instruction in question
+ * @param resultReg {@code null-ok;} the real result to use (ignore the insn's)
+ * @return {@code 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 {
+ /** {@code non-null;} list of output instructions in-progress */
+ private final OutputCollector output;
+
+ /** {@code non-null;} basic block being worked on */
+ private BasicBlock block;
+
+ /**
+ * {@code 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 {@code non-null;} destination for instruction output
+ */
+ public TranslationVisitor(OutputCollector output) {
+ this.output = output;
+ }
+
+ /**
+ * Sets the block currently being worked on.
+ *
+ * @param block {@code non-null;} the block
+ * @param lastAddress {@code 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 {@code 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 {@code non-null;} instruction to add
+ */
+ protected void addOutput(DalvInsn insn) {
+ output.add(insn);
+ }
+
+ /**
+ * Adds to the output suffix.
+ *
+ * @param insn {@code 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 {
+ /** {@code non-null;} local variable info */
+ private LocalVariableInfo locals;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param output {@code non-null;} destination for instruction output
+ * @param locals {@code 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 LocalStart} to the output if the given
+ * instruction in fact introduces a local variable.
+ *
+ * @param insn {@code non-null;} instruction in question
+ */
+ public void addIntroductionIfNecessary(Insn insn) {
+ RegisterSpec spec = locals.getAssignment(insn);
+
+ if (spec != null) {
+ addOutput(new LocalStart(insn.getPosition(), spec));
+ }
+ }
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/SimpleInsn.java b/dexgen/src/com/android/dexgen/dex/code/SimpleInsn.java
new file mode 100644
index 0000000..abef242
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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}).
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code 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/dexgen/src/com/android/dexgen/dex/code/StdCatchBuilder.java b/dexgen/src/com/android/dexgen/dex/code/StdCatchBuilder.java
new file mode 100644
index 0000000..ba149e7
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/StdCatchBuilder.java
@@ -0,0 +1,316 @@
+/*
+ * 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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.BasicBlock;
+import com.android.dexgen.rop.code.BasicBlockList;
+import com.android.dexgen.rop.code.RopMethod;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.rop.type.TypeList;
+import com.android.dexgen.util.IntList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Constructor of {@link CatchTable} instances from {@link RopMethod}
+ * and associated data.
+ */
+public final class StdCatchBuilder implements CatchBuilder {
+ /** the maximum range of a single catch handler, in code units */
+ private static final int MAX_CATCH_RANGE = 65535;
+
+ /** {@code non-null;} method to build the list for */
+ private final RopMethod method;
+
+ /** {@code non-null;} block output order */
+ private final int[] order;
+
+ /** {@code 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 {@code non-null;} method to build the list for
+ * @param order {@code non-null;} block output order
+ * @param addresses {@code non-null;} address objects for each block
+ */
+ public StdCatchBuilder(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;
+ }
+
+ /** {@inheritDoc} */
+ public CatchTable build() {
+ return build(method, order, addresses);
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasAnyCatches() {
+ 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;
+ }
+
+ /** {@inheritDoc} */
+ 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 {@code non-null;} method to build the list for
+ * @param order {@code non-null;} block output order
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code 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 {@code non-null;} block to get entries for
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code 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 {@code non-null;} the start block for the range (inclusive)
+ * @param end {@code non-null;} the start block for the range (also inclusive)
+ * @param handlers {@code non-null;} the handlers for the range
+ * @param addresses {@code 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 {@code non-null;} the start block for the range (inclusive)
+ * @param end {@code non-null;} the start block for the range (also inclusive)
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code true} 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/dexgen/src/com/android/dexgen/dex/code/SwitchData.java b/dexgen/src/com/android/dexgen/dex/code/SwitchData.java
new file mode 100644
index 0000000..a7d8465
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+import com.android.dexgen.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 {
+ /**
+ * {@code non-null;} address representing the instruction that uses this
+ * instance
+ */
+ private final CodeAddress user;
+
+ /** {@code non-null;} sorted list of switch cases (keys) */
+ private final IntList cases;
+
+ /**
+ * {@code 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}).
+ *
+ * @param position {@code non-null;} source position
+ * @param user {@code non-null;} address representing the instruction that
+ * uses this instance
+ * @param cases {@code non-null;} sorted list of switch cases (keys)
+ * @param targets {@code 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} 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 {@code non-null;} sorted list of cases
+ * @return {@code >= -1;} the packed table size or {@code -1} 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 {@code non-null;} sorted list of cases
+ * @return {@code > 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 {@code non-null;} sorted list of cases
+ * @return {@code true} 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/dexgen/src/com/android/dexgen/dex/code/TargetInsn.java b/dexgen/src/com/android/dexgen/dex/code/TargetInsn.java
new file mode 100644
index 0000000..8e02255
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+
+/**
+ * Instruction which has a single branch target.
+ */
+public final class TargetInsn extends FixedSizeInsn {
+ /** {@code non-null;} the branch target */
+ private CodeAddress target;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}), and the target is initially
+ * {@code null}.
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ * @param target {@code 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} test becomes a {@code ge}), 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 {@code non-null;} the new branch target
+ * @return {@code 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 {@code 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()}.
+ *
+ * @return {@code >= 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()}.
+ *
+ * @return the branch offset
+ */
+ public int getTargetOffset() {
+ return target.getAddress() - getAddress();
+ }
+
+ /**
+ * Returns whether the target offset is known.
+ *
+ * @return {@code true} if the target offset is known or
+ * {@code false} if not
+ */
+ public boolean hasTargetOffset() {
+ return hasAddress() && target.hasAddress();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ if (target == null) {
+ return "????";
+ }
+
+ return target.identifierString();
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/VariableSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/VariableSizeInsn.java
new file mode 100644
index 0000000..baa62a3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.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}).
+ *
+ * @param position {@code non-null;} source position
+ * @param registers {@code 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/dexgen/src/com/android/dexgen/dex/code/ZeroSizeInsn.java b/dexgen/src/com/android/dexgen/dex/code/ZeroSizeInsn.java
new file mode 100644
index 0000000..3c8c94a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code;
+
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.code.SourcePosition;
+import com.android.dexgen.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}).
+ *
+ * @param position {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form10t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form10t.java
new file mode 100644
index 0000000..4784fc3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10t}. See the instruction format spec
+ * for details.
+ */
+public final class Form10t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form10x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form10x.java
new file mode 100644
index 0000000..63c861c
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10x}. See the instruction format spec
+ * for details.
+ */
+public final class Form10x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form11n.java b/dexgen/src/com/android/dexgen/dex/code/form/Form11n.java
new file mode 100644
index 0000000..511d7d1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11n}. See the instruction format spec
+ * for details.
+ */
+public final class Form11n extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form11x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form11x.java
new file mode 100644
index 0000000..8bf9bba
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11x}. See the instruction format spec
+ * for details.
+ */
+public final class Form11x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form12x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form12x.java
new file mode 100644
index 0000000..d55a66a
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.HighRegisterPrefix;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 12x}. See the instruction format spec
+ * for details.
+ */
+public final class Form12x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form20t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form20t.java
new file mode 100644
index 0000000..2760606
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 20t}. See the instruction format spec
+ * for details.
+ */
+public final class Form20t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form21c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21c.java
new file mode 100644
index 0000000..33df3d6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21c}. See the instruction format spec
+ * for details.
+ */
+public final class Form21c extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form21h.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21h.java
new file mode 100644
index 0000000..ee6ed3e
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21h}. See the instruction format spec
+ * for details.
+ */
+public final class Form21h extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form21s.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21s.java
new file mode 100644
index 0000000..4b853d0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21s}. See the instruction format spec
+ * for details.
+ */
+public final class Form21s extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form21t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form21t.java
new file mode 100644
index 0000000..61599f6
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21t}. See the instruction format spec
+ * for details.
+ */
+public final class Form21t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form22b.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22b.java
new file mode 100644
index 0000000..6c37d57
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22b}. See the instruction format spec
+ * for details.
+ */
+public final class Form22b extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form22c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22c.java
new file mode 100644
index 0000000..b089ec4
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22c}. See the instruction format spec
+ * for details.
+ */
+public final class Form22c extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form22s.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22s.java
new file mode 100644
index 0000000..0eca280
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22s}. See the instruction format spec
+ * for details.
+ */
+public final class Form22s extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form22t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22t.java
new file mode 100644
index 0000000..707bb97
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22t}. See the instruction format spec
+ * for details.
+ */
+public final class Form22t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form22x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form22x.java
new file mode 100644
index 0000000..bd6a8df
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22x}. See the instruction format spec
+ * for details.
+ */
+public final class Form22x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form23x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form23x.java
new file mode 100644
index 0000000..eaf1cee
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 23x}. See the instruction format spec
+ * for details.
+ */
+public final class Form23x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form30t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form30t.java
new file mode 100644
index 0000000..0909ec8
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 30t}. See the instruction format spec
+ * for details.
+ */
+public final class Form30t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form31c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31c.java
new file mode 100644
index 0000000..c87a451
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstFieldRef;
+import com.android.dexgen.rop.cst.CstString;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31c}. See the instruction format spec
+ * for details.
+ */
+public final class Form31c extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form31i.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31i.java
new file mode 100644
index 0000000..e74ea86
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31i}. See the instruction format spec
+ * for details.
+ */
+public final class Form31i extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form31t.java b/dexgen/src/com/android/dexgen/dex/code/form/Form31t.java
new file mode 100644
index 0000000..212f93b
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.TargetInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31t}. See the instruction format spec
+ * for details.
+ */
+public final class Form31t extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form32x.java b/dexgen/src/com/android/dexgen/dex/code/form/Form32x.java
new file mode 100644
index 0000000..097fb65
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.dex.code.SimpleInsn;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 32x}. See the instruction format spec
+ * for details.
+ */
+public final class Form32x extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form35c.java b/dexgen/src/com/android/dexgen/dex/code/form/Form35c.java
new file mode 100644
index 0000000..147aac1
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 35c}. See the instruction format spec
+ * for details.
+ */
+public final class Form35c extends InsnFormat {
+ /** {@code 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} if the
+ * list requires more than five words or contains registers that need
+ * more than a nibble to identify them.
+ *
+ * @param regs {@code non-null;} the register list in question
+ * @return {@code >= -1;} the number of words required, or {@code -1}
+ * 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 {@code non-null;} the original list
+ * @return {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form3rc.java b/dexgen/src/com/android/dexgen/dex/code/form/Form3rc.java
new file mode 100644
index 0000000..a061c6f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 3rc}. See the instruction format spec
+ * for details.
+ */
+public final class Form3rc extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/Form51l.java b/dexgen/src/com/android/dexgen/dex/code/form/Form51l.java
new file mode 100644
index 0000000..537eaa9
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstLiteral64;
+import com.android.dexgen.rop.cst.CstLiteralBits;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 51l}. See the instruction format spec
+ * for details.
+ */
+public final class Form51l extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/code/form/SpecialFormat.java b/dexgen/src/com/android/dexgen/dex/code/form/SpecialFormat.java
new file mode 100644
index 0000000..c75f18f
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.DalvOps;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.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}.
+ */
+public final class SpecialFormat extends InsnFormat {
+ /** {@code 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/dexgen/src/com/android/dexgen/dex/file/AnnotationItem.java b/dexgen/src/com/android/dexgen/dex/file/AnnotationItem.java
new file mode 100644
index 0000000..a078bc0
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/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.dexgen.dex.file;
+
+import com.android.dexgen.rop.annotation.Annotation;
+import com.android.dexgen.rop.annotation.AnnotationVisibility;
+import com.android.dexgen.rop.annotation.NameValuePair;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstAnnotation;
+import com.android.dexgen.rop.cst.CstArray;
+import com.android.dexgen.rop.cst.CstUtf8;
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.ByteArrayAnnotatedOutput;
+
+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;
+
+ /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
+ private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
+
+ /** {@code non-null;} the annotation to represent */
+ private final Annotation annotation;
+
+ /**
+ * {@code null-ok;} type reference for the annotation type; set during
+ * {@link #addContents}
+ */
+ private TypeIdItem type;
+
+ /**
+ * {@code 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 {@code non-null;} array to sort
+ */
+ public static void sortByTypeIdIndex(AnnotationItem[] array) {
+ Arrays.sort(array, TYPE_ID_SORTER);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotation {@code 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();