summaryrefslogtreecommitdiffstats
path: root/dexlib
diff options
context:
space:
mode:
authorYohann Roussel <yroussel@google.com>2014-03-19 16:25:37 +0100
committerYohann Roussel <yroussel@google.com>2014-03-20 15:13:33 +0100
commit4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch)
treeee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /dexlib
parent3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff)
downloadtoolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'dexlib')
-rw-r--r--dexlib/Android.mk49
-rw-r--r--dexlib/NOTICE86
-rw-r--r--dexlib/README.android7
-rw-r--r--dexlib/build.gradle35
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java610
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java162
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java190
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java170
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java55
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java843
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java374
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java343
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java1348
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/CustomInlineMethodResolver.java115
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java317
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java56
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java187
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java3861
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java342
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java322
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java140
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java52
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java36
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java37
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java152
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java86
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10t.java92
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java64
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java89
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java76
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java83
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java100
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java94
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java115
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java94
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java85
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java103
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java93
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java106
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java97
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java93
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java112
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java88
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java93
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java78
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java79
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java85
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java90
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java86
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java170
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java136
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java136
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java148
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java107
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java107
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java97
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java85
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java94
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java133
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java57
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java38
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java158
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java177
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/UnknownInstruction.java47
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java62
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java71
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java99
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java126
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/InvokeInstruction.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/LiteralInstruction.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java34
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/OdexedFieldAccess.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeInline.java36
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeVirtual.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java38
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java540
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java80
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java33
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java35
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java99
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/CodeItem.java1083
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Convertible.java39
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java364
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java64
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java620
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/DexFile.java901
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java135
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java169
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedValue.java72
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java141
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedValue.java67
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java109
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java86
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java93
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java94
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java125
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java95
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java95
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java93
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java91
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java91
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java95
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java72
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java91
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java96
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java95
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java86
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java263
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/HeaderItem.java301
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/IndexedSection.java81
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Item.java224
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ItemFactory.java71
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ItemType.java123
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/MapItem.java138
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java256
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java76
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/OdexHeader.java74
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java53
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java231
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/ReadContext.java200
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Section.java221
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/StringDataItem.java169
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/StringIdItem.java177
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java211
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/TypeListItem.java288
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java178
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java41
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java91
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java71
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ByteArray.java253
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java678
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java321
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java578
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java451
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java143
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java157
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java140
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Hex.java315
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java177
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Input.java165
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java107
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java200
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Output.java141
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Pair.java40
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java52
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java366
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java261
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java347
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java258
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java64
-rw-r--r--dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java260
-rw-r--r--dexlib/version1
154 files changed, 29386 insertions, 0 deletions
diff --git a/dexlib/Android.mk b/dexlib/Android.mk
new file mode 100644
index 0000000..97a57ef
--- /dev/null
+++ b/dexlib/Android.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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)
+
+# build dexlib-jack jar
+# ============================================================
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dexlib-jack
+
+LOCAL_MODULE_TAGS := optional
+
+#LOCAL_MODULE_CLASS and LOCAL_IS_HOST_MODULE must be defined before calling $(local-intermediates-dir)
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_IS_HOST_MODULE := true
+
+intermediates := $(call local-intermediates-dir,COMMON)
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src/main/java) \
+
+#extract the current version from the pom file
+DEXLIB_VERSION := $(shell cat version)
+
+#create a new dexlib.properties file using the correct version
+$(intermediates)/resources/dexlib.properties:
+ $(hide) mkdir -p $(dir $@)
+ $(hide) echo "application.version=$(DEXLIB_VERSION)" > $@
+
+LOCAL_JAVA_RESOURCE_FILES := $(intermediates)/resources/dexlib.properties
+
+LOCAL_JAVA_LIBRARIES := \
+ guava-jack \
+ jsr305lib-jack
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/dexlib/NOTICE b/dexlib/NOTICE
new file mode 100644
index 0000000..116b080
--- /dev/null
+++ b/dexlib/NOTICE
@@ -0,0 +1,86 @@
+The majority of smali/baksmali is written and copyrighted by me (Ben Gruver)
+and released under the following license:
+
+*******************************************************************************
+Copyright (c) 2010 Ben Gruver (JesusFreke)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************
+
+
+Unless otherwise stated in the code/commit message, any changes with the
+committer of bgruv@google.com is copyrighted by Google Inc. and released
+under the following license:
+
+*******************************************************************************
+Copyright 2011, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************
+
+
+Various portions of the code are taken from the Android Open Source Project,
+and are used in accordance with the following license:
+
+*******************************************************************************
+Copyright (C) 2007 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*******************************************************************************
+
diff --git a/dexlib/README.android b/dexlib/README.android
new file mode 100644
index 0000000..9a763c6
--- /dev/null
+++ b/dexlib/README.android
@@ -0,0 +1,7 @@
+URL: https://code.google.com/p/smali/source/browse/?name=v1.4.2
+Tag: 1.4.2
+License: BSD, Apache 2
+Description: lib to manipulate dex format.
+
+This code was taken from 625b79928579
+(git clone https://code.google.com/p/smali/)
diff --git a/dexlib/build.gradle b/dexlib/build.gradle
new file mode 100644
index 0000000..3bb2eaa
--- /dev/null
+++ b/dexlib/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+dependencies {
+ compile 'com.google.code.findbugs:jsr305:1.3.9'
+ compile 'com.google.guava:guava:13.0.1'
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java
new file mode 100644
index 0000000..3882b25
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java
@@ -0,0 +1,610 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import com.google.common.base.Preconditions;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.ReadOnlyArrayList;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+
+public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
+ @Nullable
+ private AnnotationSetItem classAnnotations;
+ @Nullable
+ private FieldAnnotation[] fieldAnnotations;
+ @Nullable
+ private MethodAnnotation[] methodAnnotations;
+ @Nullable
+ private ParameterAnnotation[] parameterAnnotations;
+
+ /**
+ * Creates a new uninitialized <code>AnnotationDirectoryItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected AnnotationDirectoryItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>AnnotationDirectoryItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classAnnotations The annotations associated with the overall class
+ * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects that contain the field annotations for
+ * this class
+ * @param methodAnnotations A list of <code>MethodAnnotation</code> objects that contain the method annotations for
+ * this class
+ * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects that contain the parameter
+ * annotations for the methods in this class
+ */
+ private AnnotationDirectoryItem(DexFile dexFile, @Nullable AnnotationSetItem classAnnotations,
+ @Nullable List<FieldAnnotation> fieldAnnotations,
+ @Nullable List<MethodAnnotation> methodAnnotations,
+ @Nullable List<ParameterAnnotation> parameterAnnotations) {
+ super(dexFile);
+ this.classAnnotations = classAnnotations;
+
+ if (fieldAnnotations == null || fieldAnnotations.size() == 0) {
+ this.fieldAnnotations = null;
+ } else {
+ this.fieldAnnotations = new FieldAnnotation[fieldAnnotations.size()];
+ this.fieldAnnotations = fieldAnnotations.toArray(this.fieldAnnotations);
+ Arrays.sort(this.fieldAnnotations);
+ }
+
+ if (methodAnnotations == null || methodAnnotations.size() == 0) {
+ this.methodAnnotations = null;
+ } else {
+ this.methodAnnotations = new MethodAnnotation[methodAnnotations.size()];
+ this.methodAnnotations = methodAnnotations.toArray(this.methodAnnotations);
+ Arrays.sort(this.methodAnnotations);
+ }
+
+ if (parameterAnnotations == null || parameterAnnotations.size() == 0) {
+ this.parameterAnnotations = null;
+ } else {
+ this.parameterAnnotations = new ParameterAnnotation[parameterAnnotations.size()];
+ this.parameterAnnotations = parameterAnnotations.toArray(this.parameterAnnotations);
+ Arrays.sort(this.parameterAnnotations);
+ }
+ }
+
+ /**
+ * Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classAnnotations The annotations associated with the class
+ * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations
+ * @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations
+ * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter
+ * annotations
+ * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ */
+ public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile,
+ AnnotationSetItem classAnnotations,
+ List<FieldAnnotation> fieldAnnotations,
+ List<MethodAnnotation> methodAnnotations,
+ List<ParameterAnnotation> parameterAnnotations) {
+ AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
+ fieldAnnotations, methodAnnotations, parameterAnnotations);
+ return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
+
+ int fieldAnnotationCount = in.readInt();
+ if (fieldAnnotationCount > 0) {
+ fieldAnnotations = new FieldAnnotation[fieldAnnotationCount];
+ } else {
+ fieldAnnotations = null;
+ }
+
+ int methodAnnotationCount = in.readInt();
+ if (methodAnnotationCount > 0) {
+ methodAnnotations = new MethodAnnotation[methodAnnotationCount];
+ } else {
+ methodAnnotations = null;
+ }
+
+ int parameterAnnotationCount = in.readInt();
+ if (parameterAnnotationCount > 0) {
+ parameterAnnotations = new ParameterAnnotation[parameterAnnotationCount];
+ } else {
+ parameterAnnotations = null;
+ }
+
+ if (fieldAnnotations != null) {
+ for (int i=0; i<fieldAnnotations.length; i++) {
+ try {
+ FieldIdItem fieldIdItem = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
+ AnnotationSetItem fieldAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
+ fieldAnnotations[i] = new FieldAnnotation(fieldIdItem, fieldAnnotationSet);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "Error occured while reading FieldAnnotation at index " + i);
+ }
+ }
+ }
+
+ if (methodAnnotations != null) {
+ for (int i=0; i<methodAnnotations.length; i++) {
+ try {
+ MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
+ AnnotationSetItem methodAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
+ methodAnnotations[i] = new MethodAnnotation(methodIdItem, methodAnnotationSet);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "Error occured while reading MethodAnnotation at index " + i);
+ }
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ for (int i=0; i<parameterAnnotations.length; i++) {
+ try {
+ MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
+ AnnotationSetRefList paramaterAnnotationSet = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
+ parameterAnnotations[i] = new ParameterAnnotation(methodIdItem, paramaterAnnotationSet);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "Error occured while reading ParameterAnnotation at index " + i);
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 16 + (
+ (fieldAnnotations==null?0:fieldAnnotations.length) +
+ (methodAnnotations==null?0:methodAnnotations.length) +
+ (parameterAnnotations==null?0:parameterAnnotations.length)) * 8;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ TypeIdItem parentType = getParentType();
+ if (parentType != null) {
+ out.annotate(0, parentType.getTypeDescriptor());
+ }
+ if (classAnnotations != null) {
+ out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
+ } else {
+ out.annotate(4, "class_annotations_off:");
+ }
+
+ int length = fieldAnnotations==null?0:fieldAnnotations.length;
+ out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" +
+ length + ")");
+ length = methodAnnotations==null?0:methodAnnotations.length;
+ out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" +
+ length + ")");
+ length = parameterAnnotations==null?0:parameterAnnotations.length;
+ out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" +
+ length + ")");
+
+ int index;
+ if (fieldAnnotations != null) {
+ index = 0;
+ for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
+ out.annotate(0, "[" + index++ + "] field_annotation");
+
+ out.indent();
+ out.annotate(4, "field: " + fieldAnnotation.field.getFieldName().getStringValue() + ":" +
+ fieldAnnotation.field.getFieldType().getTypeDescriptor());
+ out.annotate(4, "annotations_off: 0x" +
+ Integer.toHexString(fieldAnnotation.annotationSet.getOffset()));
+ out.deindent();
+ }
+ }
+
+ if (methodAnnotations != null) {
+ index = 0;
+ for (MethodAnnotation methodAnnotation: methodAnnotations) {
+ out.annotate(0, "[" + index++ + "] method_annotation");
+ out.indent();
+ out.annotate(4, "method: " + methodAnnotation.method.getMethodString());
+ out.annotate(4, "annotations_off: 0x" +
+ Integer.toHexString(methodAnnotation.annotationSet.getOffset()));
+ out.deindent();
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ index = 0;
+ for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
+ out.annotate(0, "[" + index++ + "] parameter_annotation");
+ out.indent();
+ out.annotate(4, "method: " + parameterAnnotation.method.getMethodString());
+ out.annotate(4, "annotations_off: 0x" +
+ Integer.toHexString(parameterAnnotation.annotationSet.getOffset()));
+ }
+ }
+ }
+
+ out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
+ out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length);
+ out.writeInt(methodAnnotations==null?0:methodAnnotations.length);
+ out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length);
+
+ if (fieldAnnotations != null) {
+ for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
+ out.writeInt(fieldAnnotation.field.getIndex());
+ out.writeInt(fieldAnnotation.annotationSet.getOffset());
+ }
+ }
+
+ if (methodAnnotations != null) {
+ for (MethodAnnotation methodAnnotation: methodAnnotations) {
+ out.writeInt(methodAnnotation.method.getIndex());
+ out.writeInt(methodAnnotation.annotationSet.getOffset());
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
+ out.writeInt(parameterAnnotation.method.getIndex());
+ out.writeInt(parameterAnnotation.annotationSet.getOffset());
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ TypeIdItem parentType = getParentType();
+ if (parentType == null) {
+ return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
+ }
+ return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) +
+ " (" + parentType.getTypeDescriptor() + ")";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(AnnotationDirectoryItem o) {
+ Preconditions.checkNotNull(o);
+
+ TypeIdItem parentType = getParentType();
+ TypeIdItem otherParentType = o.getParentType();
+ if (parentType != null) {
+ if (otherParentType != null) {
+ return parentType.compareTo(otherParentType);
+ }
+ return 1;
+ }
+ if (otherParentType != null) {
+ return -1;
+ }
+
+ if (classAnnotations != null) {
+ if (o.classAnnotations != null) {
+ return classAnnotations.compareTo(o.classAnnotations);
+ }
+ return 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the parent type for an AnnotationDirectoryItem that is guaranteed to have a single parent, or null
+ * for one that may be referenced by multiple classes.
+ *
+ * Specifically, the AnnotationDirectoryItem may be referenced by multiple classes if it has only class annotations,
+ * but not field/method/parameter annotations.
+ *
+ * @return The parent type for this AnnotationDirectoryItem, or null if it may have multiple parents
+ */
+ @Nullable
+ public TypeIdItem getParentType() {
+ if (fieldAnnotations != null && fieldAnnotations.length > 0) {
+ return fieldAnnotations[0].field.getContainingClass();
+ }
+ if (methodAnnotations != null && methodAnnotations.length > 0) {
+ return methodAnnotations[0].method.getContainingClass();
+ }
+ if (parameterAnnotations != null && parameterAnnotations.length > 0) {
+ return parameterAnnotations[0].method.getContainingClass();
+ }
+ return null;
+ }
+
+ /**
+ * @return An <code>AnnotationSetItem</code> containing the annotations associated with this class, or null
+ * if there are no class annotations
+ */
+ @Nullable
+ public AnnotationSetItem getClassAnnotations() {
+ return classAnnotations;
+ }
+
+ /**
+ * Get a list of the field annotations in this <code>AnnotationDirectoryItem</code>
+ * @return A list of FieldAnnotation objects, or null if there are no field annotations
+ */
+ @Nonnull
+ public List<FieldAnnotation> getFieldAnnotations() {
+ if (fieldAnnotations == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(fieldAnnotations);
+ }
+
+ /**
+ * Get a list of the method annotations in this <code>AnnotationDirectoryItem</code>
+ * @return A list of MethodAnnotation objects, or null if there are no method annotations
+ */
+ @Nonnull
+ public List<MethodAnnotation> getMethodAnnotations() {
+ if (methodAnnotations == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(methodAnnotations);
+ }
+
+ /**
+ * Get a list of the parameter annotations in this <code>AnnotationDirectoryItem</code>
+ * @return A list of ParameterAnnotation objects, or null if there are no parameter annotations
+ */
+ @Nonnull
+ public List<ParameterAnnotation> getParameterAnnotations() {
+ if (parameterAnnotations == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(parameterAnnotations);
+ }
+
+ /**
+ * Gets the field annotations for the given field, or null if no annotations are defined for that field
+ * @param fieldIdItem The field to get the annotations for
+ * @return An <code>AnnotationSetItem</code> containing the field annotations, or null if none are found
+ */
+ @Nullable
+ public AnnotationSetItem getFieldAnnotations(FieldIdItem fieldIdItem) {
+ if (fieldAnnotations == null) {
+ return null;
+ }
+ int index = Arrays.binarySearch(fieldAnnotations, fieldIdItem);
+ if (index < 0) {
+ return null;
+ }
+ return fieldAnnotations[index].annotationSet;
+ }
+
+ /**
+ * Gets the method annotations for the given method, or null if no annotations are defined for that method
+ * @param methodIdItem The method to get the annotations for
+ * @return An <code>AnnotationSetItem</code> containing the method annotations, or null if none are found
+ */
+ @Nullable
+ public AnnotationSetItem getMethodAnnotations(MethodIdItem methodIdItem) {
+ if (methodAnnotations == null) {
+ return null;
+ }
+ int index = Arrays.binarySearch(methodAnnotations, methodIdItem);
+ if (index < 0) {
+ return null;
+ }
+ return methodAnnotations[index].annotationSet;
+ }
+
+ /**
+ * Gets the parameter annotations for the given method, or null if no parameter annotations are defined for that
+ * method
+ * @param methodIdItem The method to get the parameter annotations for
+ * @return An <code>AnnotationSetRefList</code> containing the parameter annotations, or null if none are found
+ */
+ @Nullable
+ public AnnotationSetRefList getParameterAnnotations(MethodIdItem methodIdItem) {
+ if (parameterAnnotations == null) {
+ return null;
+ }
+ int index = Arrays.binarySearch(parameterAnnotations, methodIdItem);
+ if (index < 0) {
+ return null;
+ }
+ return parameterAnnotations[index].annotationSet;
+ }
+
+ /**
+ *
+ */
+ public int getClassAnnotationCount() {
+ if (classAnnotations == null) {
+ return 0;
+ }
+ AnnotationItem[] annotations = classAnnotations.getAnnotations();
+ return annotations.length;
+ }
+
+ /**
+ * @return The number of field annotations in this <code>AnnotationDirectoryItem</code>
+ */
+ public int getFieldAnnotationCount() {
+ if (fieldAnnotations == null) {
+ return 0;
+ }
+ return fieldAnnotations.length;
+ }
+
+ /**
+ * @return The number of method annotations in this <code>AnnotationDirectoryItem</code>
+ */
+ public int getMethodAnnotationCount() {
+ if (methodAnnotations == null) {
+ return 0;
+ }
+ return methodAnnotations.length;
+ }
+
+ /**
+ * @return The number of parameter annotations in this <code>AnnotationDirectoryItem</code>
+ */
+ public int getParameterAnnotationCount() {
+ if (parameterAnnotations == null) {
+ return 0;
+ }
+ return parameterAnnotations.length;
+ }
+
+ @Override
+ public int hashCode() {
+ // If the item has a single parent, we can use the re-use the identity (hash) of that parent
+ TypeIdItem parentType = getParentType();
+ if (parentType != null) {
+ return parentType.hashCode();
+ }
+ if (classAnnotations != null) {
+ return classAnnotations.hashCode();
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
+ return (this.compareTo(other) == 0);
+ }
+
+ public static class FieldAnnotation implements Comparable<Convertible<FieldIdItem>>, Convertible<FieldIdItem> {
+ public final FieldIdItem field;
+ public final AnnotationSetItem annotationSet;
+
+ public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) {
+ this.field = field;
+ this.annotationSet = annotationSet;
+ }
+
+ public int compareTo(Convertible<FieldIdItem> other) {
+ return field.compareTo(other.convert());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ return compareTo((FieldAnnotation)o) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return field.hashCode() + 31 * annotationSet.hashCode();
+ }
+
+ public FieldIdItem convert() {
+ return field;
+ }
+ }
+
+ public static class MethodAnnotation implements Comparable<Convertible<MethodIdItem>>, Convertible<MethodIdItem> {
+ public final MethodIdItem method;
+ public final AnnotationSetItem annotationSet;
+
+ public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) {
+ this.method = method;
+ this.annotationSet = annotationSet;
+ }
+
+ public int compareTo(Convertible<MethodIdItem> other) {
+ return method.compareTo(other.convert());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ return compareTo((MethodAnnotation)o) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return method.hashCode() + 31 * annotationSet.hashCode();
+ }
+
+ public MethodIdItem convert() {
+ return method;
+ }
+ }
+
+ public static class ParameterAnnotation implements Comparable<Convertible<MethodIdItem>>,
+ Convertible<MethodIdItem> {
+ public final MethodIdItem method;
+ public final AnnotationSetRefList annotationSet;
+
+ public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) {
+ this.method = method;
+ this.annotationSet = annotationSet;
+ }
+
+ public int compareTo(Convertible<MethodIdItem> other) {
+ return method.compareTo(other.convert());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ return compareTo((ParameterAnnotation)o) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return method.hashCode() + 31 * annotationSet.hashCode();
+ }
+
+ public MethodIdItem convert() {
+ return method;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java
new file mode 100644
index 0000000..f35a414
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java
@@ -0,0 +1,162 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class AnnotationItem extends Item<AnnotationItem> {
+ private int hashCode = 0;
+
+ private AnnotationVisibility visibility;
+ private AnnotationEncodedSubValue annotationValue;
+
+ /**
+ * Creates a new uninitialized <code>AnnotationItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected AnnotationItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>AnnotationItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param visibility The visibility of this annotation
+ * @param annotationValue The value of this annotation
+ */
+ private AnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
+ AnnotationEncodedSubValue annotationValue) {
+ super(dexFile);
+ this.visibility = visibility;
+ this.annotationValue = annotationValue;
+ }
+
+ /**
+ * Returns an <code>AnnotationItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param visibility The visibility of this annotation
+ * @param annotationValue The value of this annotation
+ * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ */
+ public static AnnotationItem internAnnotationItem(DexFile dexFile, AnnotationVisibility visibility,
+ AnnotationEncodedSubValue annotationValue) {
+ AnnotationItem annotationItem = new AnnotationItem(dexFile, visibility, annotationValue);
+ return dexFile.AnnotationsSection.intern(annotationItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ visibility = AnnotationVisibility.fromByte(in.readByte());
+ annotationValue = new AnnotationEncodedSubValue(dexFile, in);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return annotationValue.placeValue(offset + 1);
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("visibility: " + visibility.name());
+ out.writeByte(visibility.value);
+ annotationValue.writeValue(out);
+ }else {
+ out.writeByte(visibility.value);
+ annotationValue.writeValue(out);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ANNOTATION_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "annotation_item @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(AnnotationItem o) {
+ int comp = visibility.value - o.visibility.value;
+ if (comp == 0) {
+ comp = annotationValue.compareTo(o.annotationValue);
+ }
+ return comp;
+ }
+
+ /**
+ * @return The visibility of this annotation
+ */
+ public AnnotationVisibility getVisibility() {
+ return visibility;
+ }
+
+ /**
+ * @return The encoded annotation value of this annotation
+ */
+ public AnnotationEncodedSubValue getEncodedAnnotation() {
+ return annotationValue;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = visibility.value;
+ hashCode = hashCode * 31 + annotationValue.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationItem other = (AnnotationItem)o;
+ return visibility == other.visibility && annotationValue.equals(other.annotationValue);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java
new file mode 100644
index 0000000..e221ad0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java
@@ -0,0 +1,190 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+public class AnnotationSetItem extends Item<AnnotationSetItem> {
+ private int hashCode = 0;
+
+ private AnnotationItem[] annotations;
+
+ /**
+ * Creates a new uninitialized <code>AnnotationSetItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected AnnotationSetItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>AnnotationSetItem</code> for the given annotations
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param annotations The annotations for this <code>AnnotationSetItem</code>
+ */
+ private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) {
+ super(dexFile);
+ this.annotations = annotations;
+ }
+
+ /**
+ * Returns an <code>AnnotationSetItem</code> for the given annotations, and that has been interned into the given
+ * <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param annotations The annotations for this <code>AnnotationSetItem</code>
+ * @return an <code>AnnotationSetItem</code> for the given annotations
+ */
+ public static AnnotationSetItem internAnnotationSetItem(DexFile dexFile, List<AnnotationItem> annotations) {
+ AnnotationSetItem annotationSetItem;
+ if (annotations == null) {
+ annotationSetItem = new AnnotationSetItem(dexFile, new AnnotationItem[0]);
+ } else {
+ AnnotationItem[] annotationsArray = new AnnotationItem[annotations.size()];
+ annotations.toArray(annotationsArray);
+ annotationSetItem = new AnnotationSetItem(dexFile, annotationsArray);
+ }
+ return dexFile.AnnotationSetsSection.intern(annotationSetItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ annotations = new AnnotationItem[in.readInt()];
+
+ for (int i=0; i<annotations.length; i++) {
+ annotations[i] = (AnnotationItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_ITEM,
+ in.readInt());
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4 + annotations.length * 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ Arrays.sort(annotations, new Comparator<AnnotationItem>() {
+ public int compare(AnnotationItem annotationItem, AnnotationItem annotationItem2) {
+ int annotationItemIndex = annotationItem.getEncodedAnnotation().annotationType.getIndex();
+ int annotationItemIndex2 = annotationItem2.getEncodedAnnotation().annotationType.getIndex();
+ if (annotationItemIndex < annotationItemIndex2) {
+ return -1;
+ } else if (annotationItemIndex == annotationItemIndex2) {
+ return 0;
+ }
+ return 1;
+ }
+ });
+
+
+ if (out.annotates()) {
+ out.annotate(4, "size: 0x" + Integer.toHexString(annotations.length) + " (" + annotations.length + ")");
+ for (AnnotationItem annotationItem: annotations) {
+ out.annotate(4, "annotation_off: 0x" + Integer.toHexString(annotationItem.getOffset()) + " - " +
+ annotationItem.getEncodedAnnotation().annotationType.getTypeDescriptor());
+ }
+ }
+ out.writeInt(annotations.length);
+ for (AnnotationItem annotationItem: annotations) {
+ out.writeInt(annotationItem.getOffset());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ANNOTATION_SET_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "annotation_set_item @0x" + Integer.toHexString(getOffset());
+ }
+
+/** {@inheritDoc} */
+ public int compareTo(AnnotationSetItem o) {
+ if (o == null) {
+ return 1;
+ }
+
+ int comp = annotations.length - o.annotations.length;
+ if (comp == 0) {
+ for (int i=0; i<annotations.length; i++) {
+ comp = annotations[i].compareTo(o.annotations[i]);
+ if (comp != 0) {
+ return comp;
+ }
+ }
+ }
+ return comp;
+ }
+
+ /**
+ * @return An array of the <code>AnnotationItem</code> objects in this <code>AnnotationSetItem</code>
+ */
+ public AnnotationItem[] getAnnotations() {
+ return annotations;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = 0;
+ for (AnnotationItem annotationItem: annotations) {
+ hashCode = hashCode * 31 + annotationItem.hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationSetItem other = (AnnotationSetItem)o;
+ return (this.compareTo(other) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java
new file mode 100644
index 0000000..e38ce31
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java
@@ -0,0 +1,170 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+import java.util.List;
+
+public class AnnotationSetRefList extends Item<AnnotationSetRefList> {
+ private int hashCode = 0;
+
+ private AnnotationSetItem[] annotationSets;
+
+ /**
+ * Creates a new uninitialized <code>AnnotationSetRefList</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected AnnotationSetRefList(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>AnnotationSetRefList</code> for the given annotation sets
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param annotationSets The annotationSets for this <code>AnnotationSetRefList</code>
+ */
+ private AnnotationSetRefList(DexFile dexFile, AnnotationSetItem[] annotationSets) {
+ super(dexFile);
+ this.annotationSets = annotationSets;
+ }
+
+ /**
+ * Returns an <code>AnnotationSetRefList</code> for the given annotation sets, and that has been interned into the
+ * given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param annotationSets The annotation sets for this <code>AnnotationSetRefList</code>
+ * @return an <code>AnnotationSetItem</code> for the given annotations
+ */
+ public static AnnotationSetRefList internAnnotationSetRefList(DexFile dexFile,
+ List<AnnotationSetItem> annotationSets) {
+ AnnotationSetItem[] annotationSetsArray = new AnnotationSetItem[annotationSets.size()];
+ annotationSets.toArray(annotationSetsArray);
+ AnnotationSetRefList annotationSetRefList = new AnnotationSetRefList(dexFile, annotationSetsArray);
+ return dexFile.AnnotationSetRefListsSection.intern(annotationSetRefList);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ annotationSets = new AnnotationSetItem[in.readInt()];
+
+ for (int i=0; i<annotationSets.length; i++) {
+ annotationSets[i] = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4 + annotationSets.length * 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "size: 0x" + Integer.toHexString(annotationSets.length) + " (" + annotationSets.length +
+ ")");
+ for (AnnotationSetItem annotationSetItem: annotationSets) {
+ out.annotate(4, "annotation_set_off: 0x" + Integer.toHexString(annotationSetItem.getOffset()));
+ }
+ }
+ out.writeInt(annotationSets.length);
+ for (AnnotationSetItem annotationSetItem: annotationSets) {
+ out.writeInt(annotationSetItem.getOffset());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ANNOTATION_SET_REF_LIST;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "annotation_set_item @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(AnnotationSetRefList o) {
+ int comp = annotationSets.length - o.annotationSets.length;
+ if (comp != 0) {
+ return comp;
+ }
+
+ for (int i=0; i<annotationSets.length; i++) {
+ comp = annotationSets[i].compareTo(o.annotationSets[i]);
+ if (comp != 0) {
+ return comp;
+ }
+ }
+
+ return comp;
+ }
+
+ /**
+ * @return An array of the <code>AnnotationSetItem</code> objects that make up this
+ * <code>AnnotationSetRefList</code>
+ */
+ public AnnotationSetItem[] getAnnotationSets() {
+ return annotationSets;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = 0;
+ for (AnnotationSetItem annotationSetItem: annotationSets) {
+ hashCode = hashCode * 31 + annotationSetItem.hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ AnnotationSetRefList other = (AnnotationSetRefList)o;
+ return (this.compareTo(other) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java b/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java
new file mode 100644
index 0000000..03ae96f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java
@@ -0,0 +1,55 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+public enum AnnotationVisibility {
+ BUILD((byte)0, "build"),
+ RUNTIME((byte)1, "runtime"),
+ SYSTEM((byte)2, "system");
+
+ public final byte value;
+ public final String visibility;
+ private AnnotationVisibility(byte value, String visibility) {
+ this.value = value;
+ this.visibility = visibility;
+ }
+
+ public static AnnotationVisibility fromByte(byte value) {
+ switch (value) {
+ case (byte)0:
+ return BUILD;
+ case (byte)1:
+ return RUNTIME;
+ case (byte)2:
+ return SYSTEM;
+ default:
+ throw new RuntimeException("Invalid annotation visibility value: " + value);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java
new file mode 100644
index 0000000..7afba47
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java
@@ -0,0 +1,843 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import com.google.common.base.Preconditions;
+import org.jf.dexlib.Util.*;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+
+public class ClassDataItem extends Item<ClassDataItem> {
+ @Nullable
+ private EncodedField[] staticFields = null;
+ @Nullable
+ private EncodedField[] instanceFields = null;
+ @Nullable
+ private EncodedMethod[] directMethods = null;
+ @Nullable
+ private EncodedMethod[] virtualMethods = null;
+
+ /**
+ * Creates a new uninitialized <code>ClassDataItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ public ClassDataItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>ClassDataItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param staticFields The static fields for this class
+ * @param instanceFields The instance fields for this class
+ * @param directMethods The direct methods for this class
+ * @param virtualMethods The virtual methods for this class
+ */
+ private ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields,
+ @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods,
+ @Nullable EncodedMethod[] virtualMethods) {
+ super(dexFile);
+ this.staticFields = staticFields;
+ this.instanceFields = instanceFields;
+ this.directMethods = directMethods;
+ this.virtualMethods = virtualMethods;
+ }
+
+ /**
+ * Creates a new <code>ClassDataItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param staticFields The static fields for this class
+ * @param instanceFields The instance fields for this class
+ * @param directMethods The direct methods for this class
+ * @param virtualMethods The virtual methods for this class
+ * @return a new <code>ClassDataItem</code> with the given values
+ */
+ public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List<EncodedField> staticFields,
+ @Nullable List<EncodedField> instanceFields,
+ @Nullable List<EncodedMethod> directMethods,
+ @Nullable List<EncodedMethod> virtualMethods) {
+ EncodedField[] staticFieldsArray = null;
+ EncodedField[] instanceFieldsArray = null;
+ EncodedMethod[] directMethodsArray = null;
+ EncodedMethod[] virtualMethodsArray = null;
+
+ if (staticFields != null && staticFields.size() > 0) {
+ SortedSet<EncodedField> staticFieldsSet = new TreeSet<EncodedField>();
+ for (EncodedField staticField: staticFields) {
+ if (staticFieldsSet.contains(staticField)) {
+ System.err.println(String.format("Ignoring duplicate static field definition: %s",
+ staticField.field.getFieldString()));
+ continue;
+ }
+ staticFieldsSet.add(staticField);
+ }
+
+ staticFieldsArray = new EncodedField[staticFieldsSet.size()];
+ staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray);
+ }
+
+ if (instanceFields != null && instanceFields.size() > 0) {
+ SortedSet<EncodedField> instanceFieldsSet = new TreeSet<EncodedField>();
+ for (EncodedField instanceField: instanceFields) {
+ if (instanceFieldsSet.contains(instanceField)) {
+ System.err.println(String.format("Ignoring duplicate instance field definition: %s",
+ instanceField.field.getFieldString()));
+ continue;
+ }
+ instanceFieldsSet.add(instanceField);
+ }
+
+ instanceFieldsArray = new EncodedField[instanceFieldsSet.size()];
+ instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray);
+ }
+
+ TreeSet<EncodedMethod> directMethodSet = new TreeSet<EncodedMethod>();
+
+ if (directMethods != null && directMethods.size() > 0) {
+ for (EncodedMethod directMethod: directMethods) {
+ if (directMethodSet.contains(directMethod)) {
+ System.err.println(String.format("Ignoring duplicate direct method definition: %s",
+ directMethod.method.getMethodString()));
+ continue;
+ }
+ directMethodSet.add(directMethod);
+ }
+
+ directMethodsArray = new EncodedMethod[directMethodSet.size()];
+ directMethodsArray = directMethodSet.toArray(directMethodsArray);
+ }
+
+ if (virtualMethods != null && virtualMethods.size() > 0) {
+ TreeSet<EncodedMethod> virtualMethodSet = new TreeSet<EncodedMethod>();
+ for (EncodedMethod virtualMethod: virtualMethods) {
+ if (directMethodSet.contains(virtualMethod)) {
+ // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined,
+ // so we can't gracefully handle this case, like we can if the duplicates are all direct or all
+ // virtual -- in which case, we ignore all but the first definition
+ throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s",
+ virtualMethod.method.getMethodString()));
+ }
+ if (virtualMethodSet.contains(virtualMethod)) {
+ System.err.println(String.format("Ignoring duplicate virtual method definition: %s",
+ virtualMethod.method.getMethodString()));
+ continue;
+ }
+ virtualMethodSet.add(virtualMethod);
+ }
+
+ virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()];
+ virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray);
+ }
+
+ ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray,
+ directMethodsArray, virtualMethodsArray);
+ return dexFile.ClassDataSection.intern(classDataItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int staticFieldsCount = in.readUnsignedLeb128();
+ int instanceFieldsCount = in.readUnsignedLeb128();
+ int directMethodsCount = in.readUnsignedLeb128();
+ int virtualMethodsCount = in.readUnsignedLeb128();
+
+ if (staticFieldsCount > 0) {
+ staticFields = new EncodedField[staticFieldsCount];
+ EncodedField previousEncodedField = null;
+ for (int i=0; i<staticFieldsCount; i++) {
+ try {
+ staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading static field at index " + i);
+ }
+ }
+ }
+
+ if (instanceFieldsCount > 0) {
+ instanceFields = new EncodedField[instanceFieldsCount];
+ EncodedField previousEncodedField = null;
+ for (int i=0; i<instanceFieldsCount; i++) {
+ try {
+ instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading instance field at index " + i);
+ }
+ }
+ }
+
+ if (directMethodsCount > 0) {
+ directMethods = new EncodedMethod[directMethodsCount];
+ EncodedMethod previousEncodedMethod = null;
+ for (int i=0; i<directMethodsCount; i++) {
+ try {
+ directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
+ previousEncodedMethod);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading direct method at index " + i);
+ }
+ }
+ }
+
+ if (virtualMethodsCount > 0) {
+ virtualMethods = new EncodedMethod[virtualMethodsCount];
+ EncodedMethod previousEncodedMethod = null;
+ for (int i=0; i<virtualMethodsCount; i++) {
+ try {
+ virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
+ previousEncodedMethod);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading virtual method at index " + i);
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ offset += Leb128Utils.unsignedLeb128Size(getStaticFieldCount());
+ offset += Leb128Utils.unsignedLeb128Size(getInstanceFieldCount());
+ offset += Leb128Utils.unsignedLeb128Size(getDirectMethodCount());
+ offset += Leb128Utils.unsignedLeb128Size(getVirtualMethodCount());
+
+ if (staticFields != null) {
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: staticFields) {
+ offset = encodedField.place(offset, previousEncodedField);
+ previousEncodedField = encodedField;
+ }
+ }
+
+ if (instanceFields != null) {
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: instanceFields) {
+ offset = encodedField.place(offset, previousEncodedField);
+ previousEncodedField = encodedField;
+ }
+ }
+
+ if (directMethods != null) {
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: directMethods) {
+ offset = encodedMethod.place(offset, previousEncodedMethod);
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+
+ if (virtualMethods != null) {
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: virtualMethods) {
+ offset = encodedMethod.place(offset, previousEncodedMethod);
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ int staticFieldCount = getStaticFieldCount();
+ out.annotate("static_fields_size: 0x" + Integer.toHexString(staticFieldCount) + " (" +
+ staticFieldCount + ")");
+ out.writeUnsignedLeb128(staticFieldCount);
+
+ int instanceFieldCount = getInstanceFieldCount();
+ out.annotate("instance_fields_size: 0x" + Integer.toHexString(instanceFieldCount) + " (" +
+ instanceFieldCount + ")");
+ out.writeUnsignedLeb128(instanceFieldCount);
+
+ int directMethodCount = getDirectMethodCount();
+ out.annotate("direct_methods_size: 0x" + Integer.toHexString(directMethodCount) + " (" +
+ directMethodCount + ")");
+ out.writeUnsignedLeb128(directMethodCount);
+
+ int virtualMethodCount = getVirtualMethodCount();
+ out.annotate("virtual_methods_size: 0x" + Integer.toHexString(virtualMethodCount) + " (" +
+ virtualMethodCount + ")");
+ out.writeUnsignedLeb128(virtualMethodCount);
+
+
+ if (staticFields != null) {
+ int index = 0;
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: staticFields) {
+ out.annotate("[" + index++ + "] static_field");
+ out.indent();
+ encodedField.writeTo(out, previousEncodedField);
+ out.deindent();
+ previousEncodedField = encodedField;
+ }
+ }
+
+ if (instanceFields != null) {
+ int index = 0;
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: instanceFields) {
+ out.annotate("[" + index++ + "] instance_field");
+ out.indent();
+ encodedField.writeTo(out, previousEncodedField);
+ out.deindent();
+ previousEncodedField = encodedField;
+ }
+ }
+
+ if (directMethods != null) {
+ int index = 0;
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: directMethods) {
+ out.annotate("[" + index++ + "] direct_method");
+ out.indent();
+ encodedMethod.writeTo(out, previousEncodedMethod);
+ out.deindent();
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+
+ if (virtualMethods != null) {
+ int index = 0;
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: virtualMethods) {
+ out.annotate("[" + index++ + "] virtual_method");
+ out.indent();
+ encodedMethod.writeTo(out, previousEncodedMethod);
+ out.deindent();
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+ } else {
+ out.writeUnsignedLeb128(getStaticFieldCount());
+ out.writeUnsignedLeb128(getInstanceFieldCount());
+ out.writeUnsignedLeb128(getDirectMethodCount());
+ out.writeUnsignedLeb128(getVirtualMethodCount());
+
+ if (staticFields != null) {
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: staticFields) {
+ encodedField.writeTo(out, previousEncodedField);
+ previousEncodedField = encodedField;
+ }
+ }
+
+
+ if (instanceFields != null) {
+ EncodedField previousEncodedField = null;
+ for (EncodedField encodedField: instanceFields) {
+ encodedField.writeTo(out, previousEncodedField);
+ previousEncodedField = encodedField;
+ }
+ }
+
+ if (directMethods != null) {
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: directMethods) {
+ encodedMethod.writeTo(out, previousEncodedMethod);
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+
+ if (virtualMethods != null) {
+ EncodedMethod previousEncodedMethod = null;
+ for (EncodedMethod encodedMethod: virtualMethods) {
+ encodedMethod.writeTo(out, previousEncodedMethod);
+ previousEncodedMethod = encodedMethod;
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_CLASS_DATA_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ TypeIdItem parentType = getParentType();
+ if (parentType == null) {
+ return "class_data_item @0x" + Integer.toHexString(getOffset());
+ }
+ return "class_data_item @0x" + Integer.toHexString(getOffset()) + " (" + parentType.getTypeDescriptor() +")";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ClassDataItem other) {
+ Preconditions.checkNotNull(other);
+
+ // An empty CodeDataItem may be shared by multiple ClassDefItems, so we can't use parent in this case
+ if (isEmpty()) {
+ if (other.isEmpty()) {
+ return 0;
+ }
+ return -1;
+ }
+ if (other.isEmpty()) {
+ return 1;
+ }
+
+ TypeIdItem parentType = getParentType();
+ TypeIdItem otherParentType= other.getParentType();
+ if (parentType == null) {
+ if (otherParentType == null) {
+ return 0;
+ }
+ return -1;
+ }
+ if (otherParentType == null) {
+ return 1;
+ }
+ return parentType.compareTo(otherParentType);
+ }
+
+ @Override
+ public int hashCode() {
+ // If the item has a single parent, we can use the re-use the identity (hash) of that parent
+ TypeIdItem parentType = getParentType();
+ if (parentType != null) {
+ return parentType.hashCode();
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the parent type for a non-empty ClassDataItem, or null for an empty one (which could be referenced by
+ * multiple ClassDefItem parents)
+ *
+ * Only an empty ClassDataItem may have multiple parents.
+ *
+ * @return The parent type for this ClassDefItem, or null if it may have multiple parents
+ */
+ @Nullable
+ public TypeIdItem getParentType() {
+ if (staticFields != null && staticFields.length > 0) {
+ return staticFields[0].field.getContainingClass();
+ }
+ if (instanceFields != null && instanceFields.length > 0) {
+ return instanceFields[0].field.getContainingClass();
+ }
+ if (directMethods != null && directMethods.length > 0) {
+ return directMethods[0].method.getContainingClass();
+ }
+ if (virtualMethods != null && virtualMethods.length > 0) {
+ return virtualMethods[0].method.getContainingClass();
+ }
+ return null;
+ }
+
+ /**
+ * @return the static fields for this class
+ */
+ @Nonnull
+ public List<EncodedField> getStaticFields() {
+ if (staticFields == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(staticFields);
+ }
+
+ /**
+ * @return the instance fields for this class
+ */
+ @Nonnull
+ public List<EncodedField> getInstanceFields() {
+ if (instanceFields == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(instanceFields);
+ }
+
+ /**
+ * @return the direct methods for this class
+ */
+ @Nonnull
+ public List<EncodedMethod> getDirectMethods() {
+ if (directMethods == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(directMethods);
+ }
+
+ /**
+ * @return the virtual methods for this class
+ */
+ @Nonnull
+ public List<EncodedMethod> getVirtualMethods() {
+ if (virtualMethods == null) {
+ return Collections.emptyList();
+ }
+ return ReadOnlyArrayList.of(virtualMethods);
+ }
+
+ /**
+ * @return The number of static fields in this <code>ClassDataItem</code>
+ */
+ public int getStaticFieldCount() {
+ if (staticFields == null) {
+ return 0;
+ }
+ return staticFields.length;
+ }
+
+ /**
+ * @return The number of instance fields in this <code>ClassDataItem</code>
+ */
+ public int getInstanceFieldCount() {
+ if (instanceFields == null) {
+ return 0;
+ }
+ return instanceFields.length;
+ }
+
+ /**
+ * @return The number of direct methods in this <code>ClassDataItem</code>
+ */
+ public int getDirectMethodCount() {
+ if (directMethods == null) {
+ return 0;
+ }
+ return directMethods.length;
+ }
+
+ /**
+ * @return The number of virtual methods in this <code>ClassDataItem</code>
+ */
+ public int getVirtualMethodCount() {
+ if (virtualMethods == null) {
+ return 0;
+ }
+ return virtualMethods.length;
+ }
+
+ /**
+ * @return true if this is an empty ClassDataItem
+ */
+ public boolean isEmpty() {
+ return (getStaticFieldCount() + getInstanceFieldCount() +
+ getDirectMethodCount() + getVirtualMethodCount()) == 0;
+ }
+
+ /**
+ * Performs a binary search for the definition of the specified direct method
+ * @param methodIdItem The MethodIdItem of the direct method to search for
+ * @return The EncodedMethod for the specified direct method, or null if not found
+ */
+ public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) {
+ return findMethodByMethodIdInternal(methodIdItem.index, directMethods);
+ }
+
+ /**
+ * Performs a binary search for the definition of the specified virtual method
+ * @param methodIdItem The MethodIdItem of the virtual method to search for
+ * @return The EncodedMethod for the specified virtual method, or null if not found
+ */
+ public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) {
+ return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
+ }
+
+ /**
+ * Performs a binary search for the definition of the specified method. It can be either direct or virtual
+ * @param methodIdItem The MethodIdItem of the virtual method to search for
+ * @return The EncodedMethod for the specified virtual method, or null if not found
+ */
+ public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) {
+ EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods);
+ if (encodedMethod != null) {
+ return encodedMethod;
+ }
+
+ return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods);
+ }
+
+ private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) {
+ int min = 0;
+ int max = encodedMethods.length;
+
+ while (min<max) {
+ int index = (min+max)>>1;
+
+ EncodedMethod encodedMethod = encodedMethods[index];
+
+ int encodedMethodIndex = encodedMethod.method.getIndex();
+ if (encodedMethodIndex == methodIdItemIndex) {
+ return encodedMethod;
+ } else if (encodedMethodIndex < methodIdItemIndex) {
+ if (min == index) {
+ break;
+ }
+ min = index;
+ } else {
+ if (max == index) {
+ break;
+ }
+ max = index;
+ }
+ }
+
+ return null;
+ }
+
+ public static class EncodedField implements Comparable<EncodedField> {
+ /**
+ * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
+ */
+ public final FieldIdItem field;
+
+ /**
+ * The access flags for this field
+ */
+ public final int accessFlags;
+
+ /**
+ * Constructs a new <code>EncodedField</code> with the given values
+ * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
+ * @param accessFlags The access flags for this field
+ */
+ public EncodedField(FieldIdItem field, int accessFlags) {
+ this.field = field;
+ this.accessFlags = accessFlags;
+ }
+
+ /**
+ * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in the Input object to read the <code>EncodedField</code> from
+ * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
+ * <code>EncodedField</code>.
+ */
+ private EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+ field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
+ accessFlags = in.readUnsignedLeb128();
+ }
+
+ /**
+ * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
+ * <code>EncodedField</code>.
+ */
+ private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+
+ if (out.annotates()) {
+ out.annotate("field: " + field.getFieldString());
+ out.writeUnsignedLeb128(field.getIndex() - previousIndex);
+ out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags));
+ out.writeUnsignedLeb128(accessFlags);
+ }else {
+ out.writeUnsignedLeb128(field.getIndex() - previousIndex);
+ out.writeUnsignedLeb128(accessFlags);
+ }
+ }
+
+ /**
+ * Calculates the size of this <code>EncodedField</code> and returns the offset
+ * immediately following it
+ * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code>
+ * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
+ * <code>EncodedField</code>.
+ * @return the offset immediately following this <code>EncodedField</code>
+ */
+ private int place(int offset, EncodedField previousEncodedField) {
+ int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
+
+ offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex);
+ offset += Leb128Utils.unsignedLeb128Size(accessFlags);
+ return offset;
+ }
+
+ /**
+ * Compares this <code>EncodedField</code> to another, based on the comparison of the associated
+ * <code>FieldIdItem</code>
+ * @param other The <code>EncodedField</code> to compare against
+ * @return a standard integer comparison value indicating the relationship
+ */
+ public int compareTo(EncodedField other)
+ {
+ return field.compareTo(other.field);
+ }
+
+ /**
+ * Determines if this <code>EncodedField</code> is equal to other, based on the equality of the associated
+ * <code>FieldIdItem</code>
+ * @param other The <code>EncodedField</code> to test for equality
+ * @return true if other is equal to this instance, otherwise false
+ */
+ public boolean equals(Object other) {
+ if (other instanceof EncodedField) {
+ return compareTo((EncodedField)other) == 0;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if this is a static field
+ */
+ public boolean isStatic() {
+ return (accessFlags & AccessFlags.STATIC.getValue()) != 0;
+ }
+ }
+
+ public static class EncodedMethod implements Comparable<EncodedMethod> {
+ /**
+ * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
+ */
+ public final MethodIdItem method;
+
+ /**
+ * The access flags for this method
+ */
+ public final int accessFlags;
+
+ /**
+ * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method
+ * (i.e. an abstract method)
+ */
+ public final CodeItem codeItem;
+
+ /**
+ * Constructs a new <code>EncodedMethod</code> with the given values
+ * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
+ * @param accessFlags The access flags for this method
+ * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code
+ * for this method (i.e. an abstract method)
+ */
+ public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) {
+ this.method = method;
+ this.accessFlags = accessFlags;
+ this.codeItem = codeItem;
+ if (codeItem != null) {
+ codeItem.setParent(this);
+ }
+ }
+
+ /**
+ * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading
+ * in a file
+ * @param in the Input object to read the <code>EncodedMethod</code> from
+ * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
+ * <code>EncodedMethod</code>.
+ */
+ public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+ method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
+ accessFlags = in.readUnsignedLeb128();
+ if (dexFile.skipInstructions()) {
+ in.readUnsignedLeb128();
+ codeItem = null;
+ } else {
+ codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM,
+ in.readUnsignedLeb128());
+ }
+ if (codeItem != null) {
+ codeItem.setParent(this);
+ }
+ }
+
+ /**
+ * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
+ * <code>EncodedMethod</code>.
+ */
+ private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+
+ if (out.annotates()) {
+ out.annotate("method: " + method.getMethodString());
+ out.writeUnsignedLeb128(method.getIndex() - previousIndex);
+ out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags));
+ out.writeUnsignedLeb128(accessFlags);
+ if (codeItem != null) {
+ out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset()));
+ out.writeUnsignedLeb128(codeItem.getOffset());
+ } else {
+ out.annotate("code_off: 0x0");
+ out.writeUnsignedLeb128(0);
+ }
+ }else {
+ out.writeUnsignedLeb128(method.getIndex() - previousIndex);
+ out.writeUnsignedLeb128(accessFlags);
+ out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset());
+ }
+ }
+
+ /**
+ * Calculates the size of this <code>EncodedMethod</code> and returns the offset
+ * immediately following it
+ * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code>
+ * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
+ * <code>EncodedMethod</code>.
+ * @return the offset immediately following this <code>EncodedField</code>
+ */
+ private int place(int offset, EncodedMethod previousEncodedMethod) {
+ int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
+
+ offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex);
+ offset += Leb128Utils.unsignedLeb128Size(accessFlags);
+ offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset());
+ return offset;
+ }
+
+ /**
+ * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated
+ * <code>MethodIdItem</code>
+ * @param other The <code>EncodedMethod</code> to compare against
+ * @return a standard integer comparison value indicating the relationship
+ */
+ public int compareTo(EncodedMethod other) {
+ return method.compareTo(other.method);
+ }
+
+ /**
+ * Determines if this <code>EncodedMethod</code> is equal to other, based on the equality of the associated
+ * <code>MethodIdItem</code>
+ * @param other The <code>EncodedMethod</code> to test for equality
+ * @return true if other is equal to this instance, otherwise false
+ */
+ public boolean equals(Object other) {
+ if (other instanceof EncodedMethod) {
+ return compareTo((EncodedMethod)other) == 0;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if this is a direct method
+ */
+ public boolean isDirect() {
+ return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
+ AccessFlags.CONSTRUCTOR.getValue())) != 0);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java b/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java
new file mode 100644
index 0000000..9664b99
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java
@@ -0,0 +1,374 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
+import org.jf.dexlib.EncodedValue.EncodedValue;
+import org.jf.dexlib.Util.AccessFlags;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.TypeUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.*;
+
+public class ClassDefItem extends Item<ClassDefItem> {
+ private TypeIdItem classType;
+ private int accessFlags;
+ private @Nullable TypeIdItem superType;
+ private @Nullable TypeListItem implementedInterfaces;
+ private @Nullable StringIdItem sourceFile;
+ private @Nullable AnnotationDirectoryItem annotations;
+ private @Nullable ClassDataItem classData;
+ private @Nullable EncodedArrayItem staticFieldInitializers;
+
+ /**
+ * Creates a new uninitialized <code>ClassDefItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected ClassDefItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>ClassDefItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType The type of this class
+ * @param accessFlags The access flags of this class
+ * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
+ * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
+ * @param sourceFile The main source file that this class is defined in, or null if not available
+ * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
+ * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
+ * @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial
+ * values should be in the same order as the static fields in the <code>ClassDataItem</code>. It can contain
+ * fewer items than static fields, in which case the remaining static fields will be initialized with a default
+ * value of null/0. The initial value for any fields that don't specifically have a value can be either the
+ * type-appropriate null/0 encoded value, or null.
+ */
+ private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, @Nullable TypeIdItem superType,
+ @Nullable TypeListItem implementedInterfaces, @Nullable StringIdItem sourceFile,
+ @Nullable AnnotationDirectoryItem annotations, @Nullable ClassDataItem classData,
+ @Nullable EncodedArrayItem staticFieldInitializers) {
+ super(dexFile);
+ assert classType != null;
+ this.classType = classType;
+ this.accessFlags = accessFlags;
+ this.superType = superType;
+ this.implementedInterfaces = implementedInterfaces;
+ this.sourceFile = sourceFile;
+ this.annotations = annotations;
+ this.classData = classData;
+ this.staticFieldInitializers = staticFieldInitializers;
+ }
+
+ /**
+ * Returns a <code>ClassDefItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType The type of this class
+ * @param accessFlags The access flags of this class
+ * @param superType The superclass of this class, or null if none (only valid for java.lang.Object)
+ * @param implementedInterfaces A list of the interfaces that this class implements, or null if none
+ * @param sourceFile The main source file that this class is defined in, or null if not available
+ * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none
+ * @param classData The <code>ClassDataItem</code> containing the method and field definitions for this class
+ * @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not
+ * null, it must contain the same number of items as the number of static fields in this class. The value in the
+ * <code>StaticFieldInitializer</code> for any field that doesn't have an explicit initial value can either be null
+ * or be the type-appropriate null/0 value.
+ * @return a <code>ClassDefItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ */
+ public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags,
+ @Nullable TypeIdItem superType, @Nullable TypeListItem implementedInterfaces,
+ @Nullable StringIdItem sourceFile, @Nullable AnnotationDirectoryItem annotations,
+ @Nullable ClassDataItem classData,
+ @Nullable List<StaticFieldInitializer> staticFieldInitializers) {
+ EncodedArrayItem encodedArrayItem = null;
+ if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) {
+ assert classData != null;
+ assert staticFieldInitializers.size() == classData.getStaticFieldCount();
+ encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers);
+ }
+
+ ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces,
+ sourceFile, annotations, classData, encodedArrayItem);
+ return dexFile.ClassDefsSection.intern(classDefItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
+ accessFlags = in.readInt();
+ superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt());
+ implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST,
+ in.readInt());
+ sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt());
+ annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset(
+ ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt());
+ classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt());
+ staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset(
+ ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 32;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "class_type: " + classType.getTypeDescriptor());
+ out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags));
+ out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor()));
+ out.annotate(4, "interfaces: " +
+ (implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" ")));
+ out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue()));
+ out.annotate(4, "annotations_off: " +
+ (annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset())));
+ out.annotate(4, "class_data_off:" +
+ (classData==null?"":"0x"+Integer.toHexString(classData.getOffset())));
+ out.annotate(4, "static_values_off: " +
+ (staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset())));
+ }
+ out.writeInt(classType.getIndex());
+ out.writeInt(accessFlags);
+ out.writeInt(superType==null?-1:superType.getIndex());
+ out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset());
+ out.writeInt(sourceFile==null?-1:sourceFile.getIndex());
+ out.writeInt(annotations==null?0:annotations.getOffset());
+ out.writeInt(classData==null?0:classData.getOffset());
+ out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_CLASS_DEF_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "class_def_item: " + classType.getTypeDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ClassDefItem o) {
+ //The actual sorting for this class is done during the placement phase, in ClassDefPlacer.
+ //This method is just used for sorting the associated ClassDataItem items after the ClassDefItems have been
+ //placed, so we can just do the comparison based on the offsets
+ return this.getOffset() - o.getOffset();
+ }
+
+ public TypeIdItem getClassType() {
+ return classType;
+ }
+
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ @Nullable
+ public TypeIdItem getSuperclass() {
+ return superType;
+ }
+
+ @Nullable
+ public TypeListItem getInterfaces() {
+ return implementedInterfaces;
+ }
+
+ @Nullable
+ public StringIdItem getSourceFile() {
+ return sourceFile;
+ }
+
+ @Nullable
+ public AnnotationDirectoryItem getAnnotations() {
+ return annotations;
+ }
+
+ @Nullable
+ public ClassDataItem getClassData() {
+ return classData;
+ }
+
+ @Nullable
+ public EncodedArrayItem getStaticFieldInitializers() {
+ return staticFieldInitializers;
+ }
+
+ public static int placeClassDefItems(IndexedSection<ClassDefItem> section, int offset) {
+ ClassDefPlacer cdp = new ClassDefPlacer(section);
+ return cdp.placeSection(offset);
+ }
+
+ /**
+ * This class places the items within a ClassDefItem section, such that superclasses and interfaces are
+ * placed before sub/implementing classes
+ */
+ private static class ClassDefPlacer {
+ private final IndexedSection<ClassDefItem> section;
+ private final HashMap<TypeIdItem, ClassDefItem> unplacedClassDefsByType =
+ new HashMap<TypeIdItem, ClassDefItem>();
+
+ private int currentIndex = 0;
+ private int currentOffset;
+
+ public ClassDefPlacer(IndexedSection<ClassDefItem> section) {
+ this.section = section;
+
+ for (ClassDefItem classDefItem: section.items) {
+ TypeIdItem typeIdItem = classDefItem.classType;
+ unplacedClassDefsByType.put(typeIdItem, classDefItem);
+ }
+ }
+
+ public int placeSection(int offset) {
+ currentOffset = offset;
+
+ if (section.DexFile.getSortAllItems()) {
+ //presort the list, to guarantee a unique ordering
+ Collections.sort(section.items, new Comparator<ClassDefItem>() {
+ public int compare(ClassDefItem a, ClassDefItem b) {
+ return a.getClassType().compareTo(b.getClassType());
+ }
+ });
+ }
+
+ //we need to initialize the offset for all the classes to -1, so we can tell which ones
+ //have been placed
+ for (ClassDefItem classDefItem: section.items) {
+ classDefItem.offset = -1;
+ }
+
+ for (ClassDefItem classDefItem: section.items) {
+ placeClass(classDefItem);
+ }
+
+ for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) {
+ section.items.set(classDefItem.getIndex(), classDefItem);
+ }
+
+ return currentOffset;
+ }
+
+ private void placeClass(ClassDefItem classDefItem) {
+ if (!classDefItem.isPlaced()) {
+ TypeIdItem superType = classDefItem.superType;
+ ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType);
+
+ if (superClassDefItem != null) {
+ placeClass(superClassDefItem);
+ }
+
+ TypeListItem interfaces = classDefItem.implementedInterfaces;
+
+ if (interfaces != null) {
+ for (TypeIdItem interfaceType: interfaces.getTypes()) {
+ ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType);
+ if (interfaceClass != null) {
+ placeClass(interfaceClass);
+ }
+ }
+ }
+
+ currentOffset = classDefItem.placeAt(currentOffset, currentIndex++);
+ unplacedClassDefsByType.remove(classDefItem.classType);
+ }
+ }
+ }
+
+ public static class StaticFieldInitializer implements Comparable<StaticFieldInitializer> {
+ public final EncodedValue value;
+ public final ClassDataItem.EncodedField field;
+ public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) {
+ this.value = value;
+ this.field = field;
+ }
+
+ public int compareTo(StaticFieldInitializer other) {
+ return field.compareTo(other.field);
+ }
+ }
+
+
+ /**
+ * A helper method to sort the static field initializers and populate the default values as needed
+ * @param dexFile the <code>DexFile</code>
+ * @param staticFieldInitializers the initial values
+ * @return an interned EncodedArrayItem containing the static field initializers
+ */
+ private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile,
+ @Nonnull List<StaticFieldInitializer> staticFieldInitializers) {
+ if (staticFieldInitializers.size() == 0) {
+ return null;
+ }
+
+ int len = staticFieldInitializers.size();
+
+ // make a copy before sorting. we don't want to modify the list passed to us
+ staticFieldInitializers = new ArrayList<StaticFieldInitializer>(staticFieldInitializers);
+ Collections.sort(staticFieldInitializers);
+
+ int lastIndex = -1;
+ for (int i=len-1; i>=0; i--) {
+ StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
+
+ if (staticFieldInitializer.value != null &&
+ (staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType(
+ staticFieldInitializer.field.field.getFieldType())) != 0)) {
+ lastIndex = i;
+ break;
+ }
+ }
+
+ //we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem
+ if (lastIndex == -1) {
+ return null;
+ }
+
+ EncodedValue[] values = new EncodedValue[lastIndex+1];
+
+ for (int i=0; i<=lastIndex; i++) {
+ StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i);
+ EncodedValue encodedValue = staticFieldInitializer.value;
+ if (encodedValue == null) {
+ encodedValue = TypeUtils.makeDefaultValueForType(staticFieldInitializer.field.field.getFieldType());
+ }
+
+ values[i] = encodedValue;
+ }
+
+ ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values);
+ return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java
new file mode 100644
index 0000000..d73abd5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java
@@ -0,0 +1,343 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.ItemType;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.Util.ExceptionWithContext;
+
+import java.util.*;
+
+public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
+ /**
+ * The actual instruction
+ */
+ protected Instruction instruction;
+
+ /**
+ * The index of the instruction, where the first instruction in the method is at index 0, and so on
+ */
+ protected final int instructionIndex;
+
+ /**
+ * Instructions that can pass on execution to this one during normal execution
+ */
+ protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
+
+ /**
+ * Instructions that can execution could pass on to next during normal execution
+ */
+ protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
+
+ /**
+ * This contains the register types *before* the instruction has executed
+ */
+ protected final RegisterType[] preRegisterMap;
+
+ /**
+ * This contains the register types *after* the instruction has executed
+ */
+ protected final RegisterType[] postRegisterMap;
+
+ /**
+ * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
+ * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
+ */
+ protected final Instruction originalInstruction;
+
+ /**
+ * An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction
+ * is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no
+ * code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed.
+ */
+ protected boolean dead = false;
+
+ public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
+ this.instruction = instruction;
+ this.originalInstruction = instruction;
+ this.instructionIndex = instructionIndex;
+ this.postRegisterMap = new RegisterType[registerCount];
+ this.preRegisterMap = new RegisterType[registerCount];
+ RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
+ for (int i=0; i<registerCount; i++) {
+ preRegisterMap[i] = unknown;
+ postRegisterMap[i] = unknown;
+ }
+ }
+
+ public int getInstructionIndex() {
+ return instructionIndex;
+ }
+
+ public int getPredecessorCount() {
+ return predecessors.size();
+ }
+
+ public SortedSet<AnalyzedInstruction> getPredecessors() {
+ return Collections.unmodifiableSortedSet(predecessors);
+ }
+
+ protected boolean addPredecessor(AnalyzedInstruction predecessor) {
+ return predecessors.add(predecessor);
+ }
+
+ protected void addSuccessor(AnalyzedInstruction successor) {
+ successors.add(successor);
+ }
+
+ protected void setDeodexedInstruction(Instruction instruction) {
+ assert originalInstruction.opcode.odexOnly();
+ this.instruction = instruction;
+ }
+
+ protected void restoreOdexedInstruction() {
+ assert originalInstruction.opcode.odexOnly();
+ instruction = originalInstruction;
+ }
+
+ public int getSuccessorCount() {
+ return successors.size();
+ }
+
+ public List<AnalyzedInstruction> getSuccesors() {
+ return Collections.unmodifiableList(successors);
+ }
+
+ public Instruction getInstruction() {
+ return instruction;
+ }
+
+ public Instruction getOriginalInstruction() {
+ return originalInstruction;
+ }
+
+ public boolean isDead() {
+ return dead;
+ }
+
+ /**
+ * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
+ * that can be the first successfully executed instruction in the method. The first instruction is always a
+ * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then
+ * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
+ * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
+ * corresponding exception handler is a beginning instruction, etc.
+ *
+ * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
+ * an instruction index of -1.
+ * @return a boolean value indicating whether this instruction is a beginning instruction
+ */
+ public boolean isBeginningInstruction() {
+ //if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an
+ //unreachable instruction.
+ if (predecessors.size() == 0) {
+ return false;
+ }
+
+ if (predecessors.first().instructionIndex == -1) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
+ * register type accordingly if it isn't a destination register for this instruction
+ * @param registerNumber Which register to set
+ * @param registerType The register type
+ * @returns true If the post-instruction register type was changed. This might be false if either the specified
+ * register is a destination register for this instruction, or if the pre-instruction register type didn't change
+ * after merging in the given register type
+ */
+ protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
+ assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
+ assert registerType != null;
+
+ RegisterType oldRegisterType = preRegisterMap[registerNumber];
+ RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
+
+ if (mergedRegisterType == oldRegisterType) {
+ return false;
+ }
+
+ preRegisterMap[registerNumber] = mergedRegisterType;
+ verifiedInstructions.clear(instructionIndex);
+
+ if (!setsRegister(registerNumber)) {
+ postRegisterMap[registerNumber] = mergedRegisterType;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
+ * given register. Any dead, unreachable, or odexed predecessor is ignored
+ * @param registerNumber the register number
+ * @return The register type resulting from merging the post-instruction register types from all predecessors
+ */
+ protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
+ RegisterType mergedRegisterType = null;
+ for (AnalyzedInstruction predecessor: predecessors) {
+ RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
+ assert predecessorRegisterType != null;
+ mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
+ }
+ return mergedRegisterType;
+ }
+
+ /*
+ * Sets the "post-instruction" register type as indicated.
+ * @param registerNumber Which register to set
+ * @param registerType The "post-instruction" register type
+ * @returns true if the given register type is different than the existing post-instruction register type
+ */
+ protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
+ assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
+ assert registerType != null;
+
+ RegisterType oldRegisterType = postRegisterMap[registerNumber];
+ if (oldRegisterType == registerType) {
+ return false;
+ }
+
+ postRegisterMap[registerNumber] = registerType;
+ return true;
+ }
+
+
+ protected boolean isInvokeInit() {
+ if (instruction == null || !instruction.opcode.canInitializeReference()) {
+ return false;
+ }
+
+ //TODO: check access flags instead of name?
+
+ InstructionWithReference instruction = (InstructionWithReference)this.instruction;
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
+ MethodIdItem method = (MethodIdItem)item;
+
+ if (!method.getMethodName().getStringValue().equals("<init>")) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean setsRegister() {
+ return instruction.opcode.setsRegister();
+ }
+
+ public boolean setsWideRegister() {
+ return instruction.opcode.setsWideRegister();
+ }
+
+ public boolean setsRegister(int registerNumber) {
+ //When constructing a new object, the register type will be an uninitialized reference after the new-instance
+ //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
+ //instructions don't normally change any registers, calling an <init> method will change the type of its
+ //object register. If the uninitialized reference has been copied to other registers, they will be initialized
+ //as well, so we need to check for that too
+ if (isInvokeInit()) {
+ int destinationRegister;
+ if (instruction instanceof FiveRegisterInstruction) {
+ destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
+ } else {
+ assert instruction instanceof RegisterRangeInstruction;
+ RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
+ assert rangeInstruction.getRegCount() > 0;
+ destinationRegister = rangeInstruction.getStartRegister();
+ }
+
+ if (registerNumber == destinationRegister) {
+ return true;
+ }
+ RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber);
+ if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
+ preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
+
+ return false;
+ }
+ //check if the uninit ref has been copied to another register
+ if (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) {
+ return true;
+ }
+ return false;
+ }
+
+ if (!setsRegister()) {
+ return false;
+ }
+ int destinationRegister = getDestinationRegister();
+
+ if (registerNumber == destinationRegister) {
+ return true;
+ }
+ if (setsWideRegister() && registerNumber == (destinationRegister + 1)) {
+ return true;
+ }
+ return false;
+ }
+
+ public int getDestinationRegister() {
+ if (!this.instruction.opcode.setsRegister()) {
+ throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
+ "store a value");
+ }
+ return ((SingleRegisterInstruction)instruction).getRegisterA();
+ }
+
+ public int getRegisterCount() {
+ return postRegisterMap.length;
+ }
+
+ public RegisterType getPostInstructionRegisterType(int registerNumber) {
+ return postRegisterMap[registerNumber];
+ }
+
+ public RegisterType getPreInstructionRegisterType(int registerNumber) {
+ return preRegisterMap[registerNumber];
+ }
+
+ public int compareTo(AnalyzedInstruction analyzedInstruction) {
+ //TODO: out of curiosity, check the disassembly of this to see if it retrieves the value of analyzedInstruction.instructionIndex for every access. It should, because the field is final. What about if we set the field to non-final?
+ if (instructionIndex < analyzedInstruction.instructionIndex) {
+ return -1;
+ } else if (instructionIndex == analyzedInstruction.instructionIndex) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java
new file mode 100644
index 0000000..794cec6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java
@@ -0,0 +1,1348 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.*;
+import org.jf.dexlib.Util.AccessFlags;
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.SparseArray;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.File;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.jf.dexlib.ClassDataItem.EncodedField;
+import static org.jf.dexlib.ClassDataItem.EncodedMethod;
+
+public class ClassPath {
+ private static ClassPath theClassPath = null;
+
+ /**
+ * The current version of dalvik in master(AOSP) has a slight change to the way the
+ * virtual tables are computed. This should be set to true to use the new logic.
+ * TODO: set this based on api level, once it's present in a released version of Android
+ */
+ private boolean checkPackagePrivateAccess;
+
+ private final HashMap<String, ClassDef> classDefs;
+ protected ClassDef javaLangObjectClassDef; //cached ClassDef for Ljava/lang/Object;
+
+ // Contains the classes that we haven't loaded yet
+ private HashMap<String, UnresolvedClassInfo> unloadedClasses;
+
+ private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
+
+ /**
+ * Initialize the class path using the dependencies from an odex file
+ * @param classPathDirs The directories to search for boot class path files
+ * @param extraBootClassPathEntries any extra entries that should be added after the entries that are read
+ * from the odex file
+ * @param dexFilePath The path of the dex file (used for error reporting purposes only)
+ * @param dexFile The DexFile to load - it must represents an odex file
+ */
+ public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
+ String dexFilePath, DexFile dexFile,
+ boolean checkPackagePrivateAccess) {
+ if (!dexFile.isOdex()) {
+ throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
+ }
+
+ if (theClassPath != null) {
+ throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
+ }
+
+ OdexDependencies odexDependencies = dexFile.getOdexDependencies();
+
+ String[] bootClassPath = new String[odexDependencies.getDependencyCount()];
+ for (int i=0; i<bootClassPath.length; i++) {
+ String dependency = odexDependencies.getDependency(i);
+
+ if (dependency.endsWith(".odex")) {
+ int slashIndex = dependency.lastIndexOf("/");
+
+ if (slashIndex != -1) {
+ dependency = dependency.substring(slashIndex+1);
+ }
+ } else if (dependency.endsWith("@classes.dex")) {
+ Matcher m = dalvikCacheOdexPattern.matcher(dependency);
+
+ if (!m.find()) {
+ throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
+ }
+
+ dependency = m.group(1);
+ } else {
+ throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
+ }
+
+ bootClassPath[i] = dependency;
+ }
+
+ theClassPath = new ClassPath();
+ theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
+ checkPackagePrivateAccess);
+ }
+
+ /**
+ * Initialize the class path using the given boot class path entries
+ * @param classPathDirs The directories to search for boot class path files
+ * @param bootClassPath A list of the boot class path entries to search for and load
+ * @param dexFilePath The path of the dex file (used for error reporting purposes only)
+ * @param dexFile the DexFile to load
+ * classes
+ */
+ public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
+ String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile,
+ boolean checkPackagePrivateAccess) {
+ if (theClassPath != null) {
+ throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
+ }
+
+ theClassPath = new ClassPath();
+ theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
+ checkPackagePrivateAccess);
+ }
+
+ private ClassPath() {
+ classDefs = new HashMap<String, ClassDef>();
+ }
+
+ private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
+ String dexFilePath, DexFile dexFile, boolean checkPackagePrivateAccess) {
+ this.checkPackagePrivateAccess = checkPackagePrivateAccess;
+ unloadedClasses = new LinkedHashMap<String, UnresolvedClassInfo>();
+
+ if (bootClassPath != null) {
+ for (String bootClassPathEntry: bootClassPath) {
+ loadBootClassPath(classPathDirs, bootClassPathEntry);
+ }
+ }
+
+ if (extraBootClassPathEntries != null) {
+ for (String bootClassPathEntry: extraBootClassPathEntries) {
+ loadBootClassPath(classPathDirs, bootClassPathEntry);
+ }
+ }
+
+ if (dexFile != null) {
+ loadDexFile(dexFilePath, dexFile);
+ }
+
+ javaLangObjectClassDef = getClassDef("Ljava/lang/Object;", false);
+
+ for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
+ ClassDef classDef = new PrimitiveClassDef(primitiveType);
+ classDefs.put(primitiveType, classDef);
+ }
+ }
+
+ private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) {
+ for (String classPathDir: classPathDirs) {
+ File file = null;
+ DexFile dexFile = null;
+
+ int extIndex = bootClassPathEntry.lastIndexOf(".");
+
+ String baseEntry;
+ if (extIndex == -1) {
+ baseEntry = bootClassPathEntry;
+ } else {
+ baseEntry = bootClassPathEntry.substring(0, extIndex);
+ }
+
+ for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) {
+ if (ext.length() == 0) {
+ file = new File(classPathDir, bootClassPathEntry);
+ } else {
+ file = new File(classPathDir, baseEntry + ext);
+ }
+
+ if (file.exists()) {
+ if (!file.canRead()) {
+ System.err.println(String.format("warning: cannot open %s for reading. Will continue " +
+ "looking.", file.getPath()));
+ continue;
+ }
+
+ try {
+ dexFile = new DexFile(file, false, true);
+ } catch (DexFile.NoClassesDexException ex) {
+ continue;
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading boot class path entry \"" +
+ bootClassPathEntry + "\".");
+ }
+ }
+ }
+ if (dexFile == null) {
+ continue;
+ }
+
+ try {
+ loadDexFile(file.getPath(), dexFile);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ String.format("Error while loading boot classpath entry %s", bootClassPathEntry));
+ }
+ return;
+ }
+ throw new ExceptionWithContext(String.format("Cannot locate boot class path file %s", bootClassPathEntry));
+ }
+
+ private void loadDexFile(String dexFilePath, DexFile dexFile) {
+ for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
+ try {
+ UnresolvedClassInfo unresolvedClassInfo = new UnresolvedClassInfo(dexFilePath, classDefItem);
+
+ if (!unloadedClasses.containsKey(unresolvedClassInfo.classType)) {
+ unloadedClasses.put(unresolvedClassInfo.classType, unresolvedClassInfo);
+ }
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s",
+ classDefItem.getClassType().getTypeDescriptor()));
+ }
+ }
+ }
+
+ /**
+ * This method loads the given class (and any dependent classes, as needed), removing them from unloadedClasses
+ * @param classType the class to load
+ * @return the newly loaded ClassDef object for the given class, or null if the class cannot be found
+ */
+ @Nullable
+ private static ClassDef loadClassDef(String classType) {
+ ClassDef classDef = null;
+
+ UnresolvedClassInfo classInfo = theClassPath.unloadedClasses.get(classType);
+ if (classInfo == null) {
+ return null;
+ }
+
+ try {
+ classDef = new ClassDef(classInfo);
+ theClassPath.classDefs.put(classDef.classType, classDef);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s",
+ classInfo.classType, classInfo.dexFilePath));
+ }
+ theClassPath.unloadedClasses.remove(classType);
+
+ return classDef;
+ }
+
+ @Nonnull
+ public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
+ ClassDef classDef = theClassPath.classDefs.get(classType);
+ if (classDef == null) {
+ //if it's an array class, try to create it
+ if (classType.charAt(0) == '[') {
+ return theClassPath.createArrayClassDef(classType);
+ } else {
+ try {
+ classDef = loadClassDef(classType);
+ if (classDef == null) {
+ throw new ExceptionWithContext(
+ String.format("Could not find definition for class %s", classType));
+ }
+ } catch (Exception ex) {
+ RuntimeException exWithContext = ExceptionWithContext.withContext(ex,
+ String.format("Error while loading ClassPath class %s", classType));
+ if (createUnresolvedClassDef) {
+ //TODO: add warning message
+ return theClassPath.createUnresolvedClassDef(classType);
+ } else {
+ throw exWithContext;
+ }
+ }
+ }
+ }
+ return classDef;
+ }
+
+ public static ClassDef getClassDef(String classType) {
+ return getClassDef(classType, true);
+ }
+
+ public static ClassDef getClassDef(TypeIdItem classType) {
+ return getClassDef(classType.getTypeDescriptor());
+ }
+
+ public static ClassDef getClassDef(TypeIdItem classType, boolean creatUnresolvedClassDef) {
+ return getClassDef(classType.getTypeDescriptor(), creatUnresolvedClassDef);
+ }
+
+ //256 [ characters
+ private static final String arrayPrefix = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[";
+ private static ClassDef getArrayClassDefByElementClassAndDimension(ClassDef classDef, int arrayDimension) {
+ return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType);
+ }
+
+ private static ClassDef unresolvedObjectClassDef = null;
+ public static ClassDef getUnresolvedObjectClassDef() {
+ if (unresolvedObjectClassDef == null) {
+ unresolvedObjectClassDef = new UnresolvedClassDef("Ljava/lang/Object;");
+ }
+ return unresolvedObjectClassDef;
+ }
+
+ private ClassDef createUnresolvedClassDef(String classType) {
+ assert classType.charAt(0) == 'L';
+
+ UnresolvedClassDef unresolvedClassDef = new UnresolvedClassDef(classType);
+ classDefs.put(classType, unresolvedClassDef);
+ return unresolvedClassDef;
+ }
+
+ private ClassDef createArrayClassDef(String arrayClassName) {
+ assert arrayClassName != null;
+ assert arrayClassName.charAt(0) == '[';
+
+ ArrayClassDef arrayClassDef = new ArrayClassDef(arrayClassName);
+ if (arrayClassDef.elementClass == null) {
+ return null;
+ }
+
+ classDefs.put(arrayClassName, arrayClassDef);
+ return arrayClassDef;
+ }
+
+ public static ClassDef getCommonSuperclass(ClassDef class1, ClassDef class2) {
+ if (class1 == class2) {
+ return class1;
+ }
+
+ if (class1 == null) {
+ return class2;
+ }
+
+ if (class2 == null) {
+ return class1;
+ }
+
+ //TODO: do we want to handle primitive types here? I don't think so.. (if not, add assert)
+
+ if (class2.isInterface) {
+ if (class1.implementsInterface(class2)) {
+ return class2;
+ }
+ return theClassPath.javaLangObjectClassDef;
+ }
+
+ if (class1.isInterface) {
+ if (class2.implementsInterface(class1)) {
+ return class1;
+ }
+ return theClassPath.javaLangObjectClassDef;
+ }
+
+ if (class1 instanceof ArrayClassDef && class2 instanceof ArrayClassDef) {
+ return getCommonArraySuperclass((ArrayClassDef)class1, (ArrayClassDef)class2);
+ }
+
+ //we've got two non-array reference types. Find the class depth of each, and then move up the longer one
+ //so that both classes are at the same class depth, and then move each class up until they match
+
+ //we don't strictly need to keep track of the class depth separately, but it's probably slightly faster
+ //to do so, rather than calling getClassDepth() many times
+ int class1Depth = class1.getClassDepth();
+ int class2Depth = class2.getClassDepth();
+
+ while (class1Depth > class2Depth) {
+ class1 = class1.superclass;
+ class1Depth--;
+ }
+
+ while (class2Depth > class1Depth) {
+ class2 = class2.superclass;
+ class2Depth--;
+ }
+
+ while (class1Depth > 0) {
+ if (class1 == class2) {
+ return class1;
+ }
+ class1 = class1.superclass;
+ class1Depth--;
+ class2 = class2.superclass;
+ class2Depth--;
+ }
+
+ return class1;
+ }
+
+ private static ClassDef getCommonArraySuperclass(ArrayClassDef class1, ArrayClassDef class2) {
+ assert class1 != class2;
+
+ //If one of the arrays is a primitive array, then the only option is to return java.lang.Object
+ //TODO: might it be possible to merge something like int[] and short[] into int[]? (I don't think so..)
+ if (class1.elementClass instanceof PrimitiveClassDef || class2.elementClass instanceof PrimitiveClassDef) {
+ return theClassPath.javaLangObjectClassDef;
+ }
+
+ //if the two arrays have the same number of dimensions, then we should return an array class with the
+ //same number of dimensions, for the common superclass of the 2 element classes
+ if (class1.arrayDimensions == class2.arrayDimensions) {
+ ClassDef commonElementClass;
+ if (class1.elementClass instanceof UnresolvedClassDef ||
+ class2.elementClass instanceof UnresolvedClassDef) {
+ commonElementClass = ClassPath.getUnresolvedObjectClassDef();
+ } else {
+ commonElementClass = getCommonSuperclass(class1.elementClass, class2.elementClass);
+ }
+ return getArrayClassDefByElementClassAndDimension(commonElementClass, class1.arrayDimensions);
+ }
+
+ //something like String[][][] and String[][] should be merged to Object[][]
+ //this also holds when the element classes aren't the same (but are both reference types)
+ int dimensions = Math.min(class1.arrayDimensions, class2.arrayDimensions);
+ return getArrayClassDefByElementClassAndDimension(theClassPath.javaLangObjectClassDef, dimensions);
+ }
+
+ public static class ArrayClassDef extends ClassDef {
+ private final ClassDef elementClass;
+ private final int arrayDimensions;
+
+ protected ArrayClassDef(String arrayClassType) {
+ super(arrayClassType, ClassDef.ArrayClassDef);
+ assert arrayClassType.charAt(0) == '[';
+
+ int i=0;
+ while (arrayClassType.charAt(i) == '[') i++;
+
+ String elementClassType = arrayClassType.substring(i);
+
+ if (i>256) {
+ throw new ExceptionWithContext("Error while creating array class for element type " + elementClassType +
+ " with " + i + " dimensions. The maximum number of dimensions is 256");
+ }
+
+ try {
+ elementClass = ClassPath.getClassDef(arrayClassType.substring(i));
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType);
+ }
+ arrayDimensions = i;
+ }
+
+ /**
+ * Returns the "base" element class of the array.
+ *
+ * For example, for a multi-dimensional array of strings ([[Ljava/lang/String;), this method would return
+ * Ljava/lang/String;
+ * @return the "base" element class of the array
+ */
+ public ClassDef getBaseElementClass() {
+ return elementClass;
+ }
+
+ /**
+ * Returns the "immediate" element class of the array.
+ *
+ * For example, for a multi-dimensional array of stings with 2 dimensions ([[Ljava/lang/String;), this method
+ * would return [Ljava/lang/String;
+ * @return the immediate element class of the array
+ */
+ public ClassDef getImmediateElementClass() {
+ if (arrayDimensions == 1) {
+ return elementClass;
+ }
+ return getArrayClassDefByElementClassAndDimension(elementClass, arrayDimensions - 1);
+ }
+
+ public int getArrayDimensions() {
+ return arrayDimensions;
+ }
+
+ @Override
+ public boolean extendsClass(ClassDef superclassDef) {
+ if (!(superclassDef instanceof ArrayClassDef)) {
+ if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) {
+ return true;
+ } else if (superclassDef.isInterface) {
+ return this.implementsInterface(superclassDef);
+ }
+ return false;
+ }
+
+ ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef;
+ if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) {
+ ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
+
+ if (baseElementClass.isInterface) {
+ return true;
+ }
+
+ return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass());
+ } else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) {
+ ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
+ if (baseElementClass.isInterface) {
+ return true;
+ }
+
+ if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+ }
+
+ public static class PrimitiveClassDef extends ClassDef {
+ protected PrimitiveClassDef(String primitiveClassType) {
+ super(primitiveClassType, ClassDef.PrimitiveClassDef);
+ assert primitiveClassType.charAt(0) != 'L' && primitiveClassType.charAt(0) != '[';
+ }
+ }
+
+ public static class UnresolvedClassDef extends ClassDef {
+ protected UnresolvedClassDef(String unresolvedClassDef) {
+ super(unresolvedClassDef, ClassDef.UnresolvedClassDef);
+ assert unresolvedClassDef.charAt(0) == 'L';
+ }
+
+ protected ValidationException unresolvedValidationException() {
+ return new ValidationException(String.format("class %s cannot be resolved.", this.getClassType()));
+ }
+
+ public ClassDef getSuperclass() {
+ return theClassPath.javaLangObjectClassDef;
+ }
+
+ public int getClassDepth() {
+ throw unresolvedValidationException();
+ }
+
+ public boolean isInterface() {
+ throw unresolvedValidationException();
+ }
+
+ public boolean extendsClass(ClassDef superclassDef) {
+ if (superclassDef != theClassPath.javaLangObjectClassDef && superclassDef != this) {
+ throw unresolvedValidationException();
+ }
+ return true;
+ }
+
+ public boolean implementsInterface(ClassDef interfaceDef) {
+ throw unresolvedValidationException();
+ }
+
+ public boolean hasVirtualMethod(String method) {
+ if (!super.hasVirtualMethod(method)) {
+ throw unresolvedValidationException();
+ }
+ return true;
+ }
+ }
+
+ public static class FieldDef {
+ public final String definingClass;
+ public final String name;
+ public final String type;
+
+ public FieldDef(String definingClass, String name, String type) {
+ this.definingClass = definingClass;
+ this.name = name;
+ this.type = type;
+ }
+ }
+
+ public static class ClassDef implements Comparable<ClassDef> {
+ private final String classType;
+ private final ClassDef superclass;
+ /**
+ * This is a list of all of the interfaces that a class implements, either directly or indirectly. It includes
+ * all interfaces implemented by the superclass, and all super-interfaces of any implemented interface. The
+ * intention is to make it easier to determine whether the class implements a given interface or not.
+ */
+ private final TreeSet<ClassDef> implementedInterfaces;
+
+ private final boolean isInterface;
+
+ private final int classDepth;
+
+ // classes can only be public or package-private. Internally, any private/protected inner class is actually
+ // package-private.
+ private final boolean isPublic;
+
+ private final VirtualMethod[] vtable;
+
+ //this maps a method name of the form method(III)Ljava/lang/String; to an integer
+ //If the value is non-negative, it is a vtable index
+ //If it is -1, it is a non-static direct method,
+ //If it is -2, it is a static method
+ private final HashMap<String, Integer> methodLookup;
+
+ private final SparseArray<FieldDef> instanceFields;
+
+ public final static int ArrayClassDef = 0;
+ public final static int PrimitiveClassDef = 1;
+ public final static int UnresolvedClassDef = 2;
+
+ private final static int DirectMethod = -1;
+ private final static int StaticMethod = -2;
+
+ /**
+ * The following fields are used only during the initial loading of classes, and are set to null afterwards
+ * TODO: free these
+ */
+
+ //This is only the virtual methods that this class declares itself.
+ private VirtualMethod[] virtualMethods;
+ //this is a list of all the interfaces that the class implements directory, or any super interfaces of those
+ //interfaces. It is generated in such a way that it is ordered in the same way as dalvik's ClassObject.iftable,
+ private LinkedHashMap<String, ClassDef> interfaceTable;
+
+ /**
+ * This constructor is used for the ArrayClassDef, PrimitiveClassDef and UnresolvedClassDef subclasses
+ * @param classType the class type
+ * @param classFlavor one of ArrayClassDef, PrimitiveClassDef or UnresolvedClassDef
+ */
+ protected ClassDef(String classType, int classFlavor) {
+ if (classFlavor == ArrayClassDef) {
+ assert classType.charAt(0) == '[';
+ this.classType = classType;
+ this.superclass = ClassPath.theClassPath.javaLangObjectClassDef;
+ implementedInterfaces = new TreeSet<ClassDef>();
+ implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;"));
+ implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;"));
+ isInterface = false;
+ isPublic = true;
+
+ vtable = superclass.vtable;
+ methodLookup = superclass.methodLookup;
+
+ instanceFields = superclass.instanceFields;
+ classDepth = 1; //1 off from java.lang.Object
+
+ virtualMethods = null;
+ interfaceTable = null;
+ } else if (classFlavor == PrimitiveClassDef) {
+ //primitive type
+ assert classType.charAt(0) != '[' && classType.charAt(0) != 'L';
+
+ this.classType = classType;
+ this.superclass = null;
+ implementedInterfaces = null;
+ isInterface = false;
+ isPublic = true;
+ vtable = null;
+ methodLookup = null;
+ instanceFields = null;
+ classDepth = 0; //TODO: maybe use -1 to indicate not applicable?
+
+ virtualMethods = null;
+ interfaceTable = null;
+ } else /*if (classFlavor == UnresolvedClassDef)*/ {
+ assert classType.charAt(0) == 'L';
+ this.classType = classType;
+ this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
+ implementedInterfaces = new TreeSet<ClassDef>();
+ isInterface = false;
+ isPublic = true;
+
+ vtable = superclass.vtable;
+ methodLookup = superclass.methodLookup;
+
+ instanceFields = superclass.instanceFields;
+ classDepth = 1; //1 off from java.lang.Object
+
+ virtualMethods = null;
+ interfaceTable = null;
+ }
+ }
+
+ protected ClassDef(UnresolvedClassInfo classInfo) {
+ classType = classInfo.classType;
+ isPublic = classInfo.isPublic;
+ isInterface = classInfo.isInterface;
+
+ superclass = loadSuperclass(classInfo);
+ if (superclass == null) {
+ classDepth = 0;
+ } else {
+ classDepth = superclass.classDepth + 1;
+ }
+
+ implementedInterfaces = loadAllImplementedInterfaces(classInfo);
+
+ //TODO: we can probably get away with only creating the interface table for interface types
+ interfaceTable = loadInterfaceTable(classInfo);
+ virtualMethods = classInfo.virtualMethods;
+ vtable = loadVtable(classInfo);
+
+ int directMethodCount = 0;
+ if (classInfo.directMethods != null) {
+ directMethodCount = classInfo.directMethods.length;
+ }
+ methodLookup = new HashMap<String, Integer>((int)Math.ceil(((vtable.length + directMethodCount)/ .7f)), .75f);
+ for (int i=0; i<vtable.length; i++) {
+ methodLookup.put(vtable[i].method, i);
+ }
+ if (directMethodCount > 0) {
+ for (int i=0; i<classInfo.directMethods.length; i++) {
+ if (classInfo.staticMethods[i]) {
+ methodLookup.put(classInfo.directMethods[i], StaticMethod);
+ } else {
+ methodLookup.put(classInfo.directMethods[i], DirectMethod);
+ }
+ }
+ }
+
+ instanceFields = loadFields(classInfo);
+ }
+
+ public String getClassType() {
+ return classType;
+ }
+
+ public ClassDef getSuperclass() {
+ return superclass;
+ }
+
+ public int getClassDepth() {
+ return classDepth;
+ }
+
+ public boolean isInterface() {
+ return this.isInterface;
+ }
+
+ public boolean isPublic() {
+ return this.isPublic;
+ }
+
+ public boolean extendsClass(ClassDef superclassDef) {
+ if (superclassDef == null) {
+ return false;
+ }
+
+ if (this == superclassDef) {
+ return true;
+ }
+
+ if (superclassDef instanceof UnresolvedClassDef) {
+ throw ((UnresolvedClassDef)superclassDef).unresolvedValidationException();
+ }
+
+ int superclassDepth = superclassDef.classDepth;
+ ClassDef ancestor = this;
+ while (ancestor.classDepth > superclassDepth) {
+ ancestor = ancestor.getSuperclass();
+ }
+
+ return ancestor == superclassDef;
+ }
+
+ /**
+ * Returns true if this class implements the given interface. This searches the interfaces that this class
+ * directly implements, any interface implemented by this class's superclasses, and any super-interface of
+ * any of these interfaces.
+ * @param interfaceDef the interface
+ * @return true if this class implements the given interface
+ */
+ public boolean implementsInterface(ClassDef interfaceDef) {
+ assert !(interfaceDef instanceof UnresolvedClassDef);
+ return implementedInterfaces.contains(interfaceDef);
+ }
+
+ public boolean hasVirtualMethod(String method) {
+ Integer val = methodLookup.get(method);
+ if (val == null || val < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public int getMethodType(String method) {
+ Integer val = methodLookup.get(method);
+ if (val == null) {
+ return -1;
+ }
+ if (val >= 0) {
+ return DeodexUtil.Virtual;
+ }
+ if (val == DirectMethod) {
+ return DeodexUtil.Direct;
+ }
+ if (val == StaticMethod) {
+ return DeodexUtil.Static;
+ }
+ throw new RuntimeException("Unexpected method type");
+ }
+
+ public FieldDef getInstanceField(int fieldOffset) {
+ return this.instanceFields.get(fieldOffset, null);
+ }
+
+ public String getVirtualMethod(int vtableIndex) {
+ if (vtableIndex < 0 || vtableIndex >= vtable.length) {
+ return null;
+ }
+ return this.vtable[vtableIndex].method;
+ }
+
+ private void swap(byte[] fieldTypes, FieldDef[] fields, int position1, int position2) {
+ byte tempType = fieldTypes[position1];
+ fieldTypes[position1] = fieldTypes[position2];
+ fieldTypes[position2] = tempType;
+
+ FieldDef tempField = fields[position1];
+ fields[position1] = fields[position2];
+ fields[position2] = tempField;
+ }
+
+ private ClassDef loadSuperclass(UnresolvedClassInfo classInfo) {
+ if (classInfo.classType.equals("Ljava/lang/Object;")) {
+ if (classInfo.superclassType != null) {
+ throw new ExceptionWithContext("Invalid superclass " +
+ classInfo.superclassType + " for Ljava/lang/Object;. " +
+ "The Object class cannot have a superclass");
+ }
+ return null;
+ } else {
+ String superclassType = classInfo.superclassType;
+ if (superclassType == null) {
+ throw new ExceptionWithContext(classInfo.classType + " has no superclass");
+ }
+
+ ClassDef superclass;
+ try {
+ superclass = ClassPath.getClassDef(superclassType);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ String.format("Could not find superclass %s", superclassType));
+ }
+
+ if (!isInterface && superclass.isInterface) {
+ throw new ValidationException("Class " + classType + " has the interface " + superclass.classType +
+ " as its superclass");
+ }
+ if (isInterface && !superclass.isInterface && superclass !=
+ ClassPath.theClassPath.javaLangObjectClassDef) {
+ throw new ValidationException("Interface " + classType + " has the non-interface class " +
+ superclass.classType + " as its superclass");
+ }
+
+ return superclass;
+ }
+ }
+
+ private TreeSet<ClassDef> loadAllImplementedInterfaces(UnresolvedClassInfo classInfo) {
+ assert classType != null;
+ assert classType.equals("Ljava/lang/Object;") || superclass != null;
+ assert classInfo != null;
+
+ TreeSet<ClassDef> implementedInterfaceSet = new TreeSet<ClassDef>();
+
+ if (superclass != null) {
+ for (ClassDef interfaceDef: superclass.implementedInterfaces) {
+ implementedInterfaceSet.add(interfaceDef);
+ }
+ }
+
+
+ if (classInfo.interfaces != null) {
+ for (String interfaceType: classInfo.interfaces) {
+ ClassDef interfaceDef;
+ try {
+ interfaceDef = ClassPath.getClassDef(interfaceType);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ String.format("Could not find interface %s", interfaceType));
+ }
+ assert interfaceDef.isInterface();
+ implementedInterfaceSet.add(interfaceDef);
+
+ interfaceDef = interfaceDef.getSuperclass();
+ while (!interfaceDef.getClassType().equals("Ljava/lang/Object;")) {
+ assert interfaceDef.isInterface();
+ implementedInterfaceSet.add(interfaceDef);
+ interfaceDef = interfaceDef.getSuperclass();
+ }
+ }
+ }
+
+ return implementedInterfaceSet;
+ }
+
+ private LinkedHashMap<String, ClassDef> loadInterfaceTable(UnresolvedClassInfo classInfo) {
+ if (classInfo.interfaces == null) {
+ return null;
+ }
+
+ LinkedHashMap<String, ClassDef> interfaceTable = new LinkedHashMap<String, ClassDef>();
+
+ for (String interfaceType: classInfo.interfaces) {
+ if (!interfaceTable.containsKey(interfaceType)) {
+ ClassDef interfaceDef;
+ try {
+ interfaceDef = ClassPath.getClassDef(interfaceType);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ String.format("Could not find interface %s", interfaceType));
+ }
+ interfaceTable.put(interfaceType, interfaceDef);
+
+ if (interfaceDef.interfaceTable != null) {
+ for (ClassDef superInterface: interfaceDef.interfaceTable.values()) {
+ if (!interfaceTable.containsKey(superInterface.classType)) {
+ interfaceTable.put(superInterface.classType, superInterface);
+ }
+ }
+ }
+ }
+ }
+
+ return interfaceTable;
+ }
+
+ //TODO: check the case when we have a package private method that overrides an interface method
+ private VirtualMethod[] loadVtable(UnresolvedClassInfo classInfo) {
+ //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
+ List<VirtualMethod> virtualMethodList = new LinkedList<VirtualMethod>();
+
+ //copy the virtual methods from the superclass
+ int methodIndex = 0;
+ if (superclass != null) {
+ for (int i=0; i<superclass.vtable.length; i++) {
+ virtualMethodList.add(superclass.vtable[i]);
+ }
+
+ assert superclass.instanceFields != null;
+ }
+
+
+ //iterate over the virtual methods in the current class, and only add them when we don't already have the
+ //method (i.e. if it was implemented by the superclass)
+ if (!this.isInterface) {
+ if (classInfo.virtualMethods != null) {
+ addToVtable(classInfo.virtualMethods, virtualMethodList);
+ }
+
+ if (interfaceTable != null) {
+ for (ClassDef interfaceDef: interfaceTable.values()) {
+ if (interfaceDef.virtualMethods == null) {
+ continue;
+ }
+
+ addToVtable(interfaceDef.virtualMethods, virtualMethodList);
+ }
+ }
+ }
+
+ VirtualMethod[] vtable = new VirtualMethod[virtualMethodList.size()];
+ for (int i=0; i<virtualMethodList.size(); i++) {
+ vtable[i] = virtualMethodList.get(i);
+ }
+
+ return vtable;
+ }
+
+ private void addToVtable(VirtualMethod[] localMethods, List<VirtualMethod> vtable) {
+ for (VirtualMethod virtualMethod: localMethods) {
+ boolean found = false;
+ for (int i=0; i<vtable.size(); i++) {
+ VirtualMethod superMethod = vtable.get(i);
+ if (superMethod.method.equals(virtualMethod.method)) {
+ if (!ClassPath.theClassPath.checkPackagePrivateAccess || this.canAccess(superMethod)) {
+ found = true;
+ vtable.set(i, virtualMethod);
+ break;
+ }
+ }
+ }
+ if (!found) {
+ vtable.add(virtualMethod);
+ }
+ }
+ }
+
+ private boolean canAccess(VirtualMethod virtualMethod) {
+ if (!virtualMethod.isPackagePrivate) {
+ return true;
+ }
+
+ String otherPackage = getPackage(virtualMethod.containingClass);
+ String ourPackage = getPackage(this.classType);
+ return otherPackage.equals(ourPackage);
+ }
+
+ private String getPackage(String classType) {
+ int lastSlash = classType.lastIndexOf('/');
+ if (lastSlash < 0) {
+ return "";
+ }
+ return classType.substring(1, lastSlash);
+ }
+
+ private int getNextFieldOffset() {
+ if (instanceFields == null || instanceFields.size() == 0) {
+ return 8;
+ }
+
+ int lastItemIndex = instanceFields.size()-1;
+ int fieldOffset = instanceFields.keyAt(lastItemIndex);
+ FieldDef lastField = instanceFields.valueAt(lastItemIndex);
+
+ switch (lastField.type.charAt(0)) {
+ case 'J':
+ case 'D':
+ return fieldOffset + 8;
+ default:
+ return fieldOffset + 4;
+ }
+ }
+
+ private SparseArray<FieldDef> loadFields(UnresolvedClassInfo classInfo) {
+ //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
+ //arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
+ //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
+
+ final byte REFERENCE = 0;
+ final byte WIDE = 1;
+ final byte OTHER = 2;
+
+ FieldDef[] fields = null;
+ //the "type" for each field in fields. 0=reference,1=wide,2=other
+ byte[] fieldTypes = null;
+
+ if (classInfo.instanceFields != null) {
+ fields = new FieldDef[classInfo.instanceFields.length];
+ fieldTypes = new byte[fields.length];
+
+ for (int i=0; i<fields.length; i++) {
+ String[] fieldInfo = classInfo.instanceFields[i];
+
+ String fieldName = fieldInfo[0];
+ String fieldType = fieldInfo[1];
+
+ fieldTypes[i] = getFieldType(fieldType);
+ fields[i] = new FieldDef(classInfo.classType, fieldName, fieldType);
+ }
+ }
+
+ if (fields == null) {
+ fields = new FieldDef[0];
+ fieldTypes = new byte[0];
+ }
+
+ //The first operation is to move all of the reference fields to the front. To do this, find the first
+ //non-reference field, then find the last reference field, swap them and repeat
+ int back = fields.length - 1;
+ int front;
+ for (front = 0; front<fields.length; front++) {
+ if (fieldTypes[front] != REFERENCE) {
+ while (back > front) {
+ if (fieldTypes[back] == REFERENCE) {
+ swap(fieldTypes, fields, front, back--);
+ break;
+ }
+ back--;
+ }
+ }
+
+ if (fieldTypes[front] != REFERENCE) {
+ break;
+ }
+ }
+
+
+ int startFieldOffset = 8;
+ if (this.superclass != null) {
+ startFieldOffset = this.superclass.getNextFieldOffset();
+ }
+
+ int fieldIndexMod;
+ if ((startFieldOffset % 8) == 0) {
+ fieldIndexMod = 0;
+ } else {
+ fieldIndexMod = 1;
+ }
+
+ //next, we need to group all the wide fields after the reference fields. But the wide fields have to be
+ //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field
+ //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in.
+ //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets
+ if (front < fields.length && (front % 2) != fieldIndexMod) {
+ if (fieldTypes[front] == WIDE) {
+ //we need to swap in a 32-bit field, so the wide fields will be correctly aligned
+ back = fields.length - 1;
+ while (back > front) {
+ if (fieldTypes[back] == OTHER) {
+ swap(fieldTypes, fields, front++, back);
+ break;
+ }
+ back--;
+ }
+ } else {
+ //there's already a 32-bit field here that we can use
+ front++;
+ }
+ }
+
+ //do the swap thing for wide fields
+ back = fields.length - 1;
+ for (; front<fields.length; front++) {
+ if (fieldTypes[front] != WIDE) {
+ while (back > front) {
+ if (fieldTypes[back] == WIDE) {
+ swap(fieldTypes, fields, front, back--);
+ break;
+ }
+ back--;
+ }
+ }
+
+ if (fieldTypes[front] != WIDE) {
+ break;
+ }
+ }
+
+ int superFieldCount = 0;
+ if (superclass != null) {
+ superFieldCount = superclass.instanceFields.size();
+ }
+
+ //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
+ int totalFieldCount = superFieldCount + fields.length;
+ SparseArray<FieldDef> instanceFields = new SparseArray<FieldDef>(totalFieldCount);
+
+ int fieldOffset;
+
+ if (superclass != null && superFieldCount > 0) {
+ for (int i=0; i<superFieldCount; i++) {
+ instanceFields.append(superclass.instanceFields.keyAt(i), superclass.instanceFields.valueAt(i));
+ }
+
+ fieldOffset = instanceFields.keyAt(superFieldCount-1);
+
+ FieldDef lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
+ char fieldType = lastSuperField.type.charAt(0);
+ if (fieldType == 'J' || fieldType == 'D') {
+ fieldOffset += 8;
+ } else {
+ fieldOffset += 4;
+ }
+ } else {
+ //the field values start at 8 bytes into the DataObject dalvik structure
+ fieldOffset = 8;
+ }
+
+ boolean gotDouble = false;
+ for (int i=0; i<fields.length; i++) {
+ FieldDef field = fields[i];
+
+ //add padding to align the wide fields, if needed
+ if (fieldTypes[i] == WIDE && !gotDouble) {
+ if (!gotDouble) {
+ if (fieldOffset % 8 != 0) {
+ assert fieldOffset % 8 == 4;
+ fieldOffset += 4;
+ }
+ gotDouble = true;
+ }
+ }
+
+ instanceFields.append(fieldOffset, field);
+ if (fieldTypes[i] == WIDE) {
+ fieldOffset += 8;
+ } else {
+ fieldOffset += 4;
+ }
+ }
+ return instanceFields;
+ }
+
+ private byte getFieldType(String fieldType) {
+ switch (fieldType.charAt(0)) {
+ case '[':
+ case 'L':
+ return 0; //REFERENCE
+ case 'J':
+ case 'D':
+ return 1; //WIDE
+ default:
+ return 2; //OTHER
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ClassDef)) return false;
+
+ ClassDef classDef = (ClassDef) o;
+
+ return classType.equals(classDef.classType);
+ }
+
+ @Override
+ public int hashCode() {
+ return classType.hashCode();
+ }
+
+ public int compareTo(ClassDef classDef) {
+ return classType.compareTo(classDef.classType);
+ }
+ }
+
+ private static class VirtualMethod {
+ public String containingClass;
+ public String method;
+ public boolean isPackagePrivate;
+ }
+
+ /**
+ * This aggregates the basic information about a class in an easy-to-use format, without requiring references
+ * to any other class.
+ */
+ private static class UnresolvedClassInfo {
+ public final String dexFilePath;
+ public final String classType;
+ public final boolean isPublic;
+ public final boolean isInterface;
+ public final String superclassType;
+ public final String[] interfaces;
+ public final boolean[] staticMethods;
+ public final String[] directMethods;
+ public final VirtualMethod[] virtualMethods;
+ public final String[][] instanceFields;
+
+ public UnresolvedClassInfo(String dexFilePath, ClassDefItem classDefItem) {
+ this.dexFilePath = dexFilePath;
+
+ classType = classDefItem.getClassType().getTypeDescriptor();
+
+ isPublic = (classDefItem.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0;
+ isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
+
+ TypeIdItem superclassType = classDefItem.getSuperclass();
+ if (superclassType == null) {
+ this.superclassType = null;
+ } else {
+ this.superclassType = superclassType.getTypeDescriptor();
+ }
+
+ interfaces = loadInterfaces(classDefItem);
+
+ ClassDataItem classDataItem = classDefItem.getClassData();
+ if (classDataItem != null) {
+ boolean[][] _staticMethods = new boolean[1][];
+ directMethods = loadDirectMethods(classDataItem, _staticMethods);
+ staticMethods = _staticMethods[0];
+ virtualMethods = loadVirtualMethods(classDataItem);
+ instanceFields = loadInstanceFields(classDataItem);
+ } else {
+ staticMethods = null;
+ directMethods = null;
+ virtualMethods = null;
+ instanceFields = null;
+ }
+ }
+
+ private String[] loadInterfaces(ClassDefItem classDefItem) {
+ TypeListItem typeList = classDefItem.getInterfaces();
+ if (typeList != null) {
+ List<TypeIdItem> types = typeList.getTypes();
+ if (types != null && types.size() > 0) {
+ String[] interfaces = new String[types.size()];
+ for (int i=0; i<interfaces.length; i++) {
+ interfaces[i] = types.get(i).getTypeDescriptor();
+ }
+ return interfaces;
+ }
+ }
+ return null;
+ }
+
+ private String[] loadDirectMethods(ClassDataItem classDataItem, boolean[][] _staticMethods) {
+ List<EncodedMethod> encodedMethods = classDataItem.getDirectMethods();
+ if (encodedMethods.size() > 0) {
+ boolean[] staticMethods = new boolean[encodedMethods.size()];
+ String[] directMethods = new String[encodedMethods.size()];
+
+ for (int i=0; i<encodedMethods.size(); i++) {
+ EncodedMethod encodedMethod = encodedMethods.get(i);
+
+ if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0) {
+ staticMethods[i] = true;
+ }
+ directMethods[i] = encodedMethod.method.getShortMethodString();
+ }
+ _staticMethods[0] = staticMethods;
+ return directMethods;
+ }
+ return null;
+ }
+
+ private VirtualMethod[] loadVirtualMethods(ClassDataItem classDataItem) {
+ List<EncodedMethod> encodedMethods = classDataItem.getVirtualMethods();
+ if (encodedMethods.size() > 0) {
+ VirtualMethod[] virtualMethods = new VirtualMethod[encodedMethods.size()];
+ for (int i=0; i<encodedMethods.size(); i++) {
+ virtualMethods[i] = new VirtualMethod();
+ EncodedMethod encodedMethod = encodedMethods.get(i);
+
+ virtualMethods[i].isPackagePrivate = methodIsPackagePrivate(encodedMethod.accessFlags);
+ virtualMethods[i].containingClass = classDataItem.getParentType().getTypeDescriptor();
+ virtualMethods[i].method = encodedMethods.get(i).method.getShortMethodString();
+ }
+ return virtualMethods;
+ }
+ return null;
+ }
+
+ private static boolean methodIsPackagePrivate(int accessFlags) {
+ return (accessFlags & (AccessFlags.PRIVATE.getValue() |
+ AccessFlags.PROTECTED.getValue() |
+ AccessFlags.PUBLIC.getValue())) == 0;
+ }
+
+ private String[][] loadInstanceFields(ClassDataItem classDataItem) {
+ List<EncodedField> encodedFields = classDataItem.getInstanceFields();
+ if (encodedFields.size() > 0) {
+ String[][] instanceFields = new String[encodedFields.size()][2];
+ for (int i=0; i<encodedFields.size(); i++) {
+ EncodedField encodedField = encodedFields.get(i);
+ instanceFields[i][0] = encodedField.field.getFieldName().getStringValue();
+ instanceFields[i][1] = encodedField.field.getFieldType().getTypeDescriptor();
+ }
+ return instanceFields;
+ }
+ return null;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/CustomInlineMethodResolver.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/CustomInlineMethodResolver.java
new file mode 100644
index 0000000..9c1653d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/CustomInlineMethodResolver.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.Code.OdexedInvokeInline;
+import org.jf.dexlib.Code.OdexedInvokeVirtual;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CustomInlineMethodResolver extends InlineMethodResolver {
+ private DeodexUtil.InlineMethod[] inlineMethods;
+
+ public CustomInlineMethodResolver(String inlineTable) {
+ FileReader fr = null;
+ try {
+ fr = new FileReader(inlineTable);
+ } catch (FileNotFoundException ex) {
+ throw new RuntimeException("Could not find inline table file: " + inlineTable);
+ }
+
+ List<String> lines = new ArrayList<String>();
+
+ BufferedReader br = new BufferedReader(fr);
+
+ try {
+ String line = br.readLine();
+
+ while (line != null) {
+ if (line.length() > 0) {
+ lines.add(line);
+ }
+
+ line = br.readLine();
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("Error while reading file: " + inlineTable, ex);
+ }
+
+ inlineMethods = new DeodexUtil.InlineMethod[lines.size()];
+
+ for (int i=0; i<inlineMethods.length; i++) {
+ inlineMethods[i] = parseAndResolveInlineMethod(lines.get(i));
+ }
+ }
+
+ @Override
+ public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) {
+ assert analyzedInstruction.instruction instanceof OdexedInvokeInline;
+
+ OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction;
+ int methodIndex = instruction.getInlineIndex();
+
+ if (methodIndex < 0 || methodIndex >= inlineMethods.length) {
+ throw new RuntimeException("Invalid method index: " + methodIndex);
+ }
+ return inlineMethods[methodIndex];
+ }
+
+ private static final Pattern longMethodPattern = Pattern.compile("(L[^;]+;)->([^(]+)\\(([^)]*)\\)(.+)");
+
+ private DeodexUtil.InlineMethod parseAndResolveInlineMethod(String inlineMethod) {
+ Matcher m = longMethodPattern.matcher(inlineMethod);
+ if (!m.matches()) {
+ assert false;
+ throw new RuntimeException("Invalid method descriptor: " + inlineMethod);
+ }
+
+ String className = m.group(1);
+ String methodName = m.group(2);
+ String methodParams = m.group(3);
+ String methodRet = m.group(4);
+
+ ClassPath.ClassDef classDef = ClassPath.getClassDef(className, false);
+ int methodType = classDef.getMethodType(String.format("%s(%s)%s", methodName, methodParams, methodRet));
+
+ if (methodType == -1) {
+ throw new RuntimeException("Cannot resolve inline method: " + inlineMethod);
+ }
+
+ return new DeodexUtil.InlineMethod(methodType, className, methodName, methodParams, methodRet);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java
new file mode 100644
index 0000000..d4cf3a8
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java
@@ -0,0 +1,317 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.*;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DeodexUtil {
+ public static final int Virtual = 0;
+ public static final int Direct = 1;
+ public static final int Static = 2;
+
+ private final InlineMethodResolver inlineMethodResolver;
+
+ public final DexFile dexFile;
+
+ public DeodexUtil(DexFile dexFile) {
+ this.dexFile = dexFile;
+ OdexHeader odexHeader = dexFile.getOdexHeader();
+ if (odexHeader == null) {
+ //if there isn't an odex header, why are we creating an DeodexUtil object?
+ assert false;
+ throw new RuntimeException("Cannot create a DeodexUtil object for a dex file without an odex header");
+ }
+ inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(this, odexHeader.version);
+ }
+
+ public DeodexUtil(DexFile dexFile, InlineMethodResolver inlineMethodResolver) {
+ this.dexFile = dexFile;
+ this.inlineMethodResolver = inlineMethodResolver;
+ }
+
+ public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) {
+ return inlineMethodResolver.resolveExecuteInline(instruction);
+ }
+
+ public FieldIdItem lookupField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
+ int fieldOffset) {
+ ClassPath.FieldDef field = instanceClass.getInstanceField(fieldOffset);
+ if (field == null) {
+ return null;
+ }
+
+ return parseAndResolveField(accessingClass, instanceClass, field);
+ }
+
+ private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
+
+ public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
+ int methodIndex) {
+ String method = instanceClass.getVirtualMethod(methodIndex);
+ if (method == null) {
+ return null;
+ }
+
+ Matcher m = shortMethodPattern.matcher(method);
+ if (!m.matches()) {
+ assert false;
+ throw new RuntimeException("Invalid method descriptor: " + method);
+ }
+
+ String methodName = m.group(1);
+ String methodParams = m.group(2);
+ String methodRet = m.group(3);
+
+ if (instanceClass instanceof ClassPath.UnresolvedClassDef) {
+ //if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual
+ //method being looked up was a method on java.lang.Object.
+ instanceClass = ClassPath.getClassDef("Ljava/lang/Object;");
+ } else if (instanceClass.isInterface()) {
+ instanceClass = instanceClass.getSuperclass();
+ assert instanceClass != null;
+ }
+
+ return parseAndResolveMethod(accessingClass, instanceClass, methodName, methodParams, methodRet);
+ }
+
+ private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
+ String methodName, String methodParams, String methodRet) {
+ StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
+ if (methodNameItem == null) {
+ return null;
+ }
+
+ LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
+
+ for (int i=0; i<methodParams.length(); i++) {
+ TypeIdItem typeIdItem;
+
+ switch (methodParams.charAt(i)) {
+ case 'Z':
+ case 'B':
+ case 'S':
+ case 'C':
+ case 'I':
+ case 'J':
+ case 'F':
+ case 'D':
+ typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i,i+1));
+ break;
+ case 'L':
+ {
+ int end = methodParams.indexOf(';', i);
+ if (end == -1) {
+ throw new RuntimeException("invalid parameter in the method");
+ }
+
+ typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
+ i = end;
+ break;
+ }
+ case '[':
+ {
+ int end;
+ int typeStart = i+1;
+ while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') {
+ typeStart++;
+ }
+ switch (methodParams.charAt(typeStart)) {
+ case 'Z':
+ case 'B':
+ case 'S':
+ case 'C':
+ case 'I':
+ case 'J':
+ case 'F':
+ case 'D':
+ end = typeStart;
+ break;
+ case 'L':
+ end = methodParams.indexOf(';', typeStart);
+ if (end == -1) {
+ throw new RuntimeException("invalid parameter in the method");
+ }
+ break;
+ default:
+ throw new RuntimeException("invalid parameter in the method");
+ }
+
+ typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
+ i = end;
+ break;
+ }
+ default:
+ throw new RuntimeException("invalid parameter in the method");
+ }
+
+ if (typeIdItem == null) {
+ return null;
+ }
+ paramList.add(typeIdItem);
+ }
+
+ TypeListItem paramListItem = null;
+ if (paramList.size() > 0) {
+ paramListItem = TypeListItem.lookupTypeListItem(dexFile, paramList);
+ if (paramListItem == null) {
+ return null;
+ }
+ }
+
+ TypeIdItem retType = TypeIdItem.lookupTypeIdItem(dexFile, methodRet);
+ if (retType == null) {
+ return null;
+ }
+
+ ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem);
+ if (protoItem == null) {
+ return null;
+ }
+
+ ClassPath.ClassDef methodClassDef = definingClass;
+
+ do {
+ TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType());
+
+ if (classTypeItem != null) {
+ MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
+ if (methodIdItem != null && checkClassAccess(accessingClass, methodClassDef)) {
+ return methodIdItem;
+ }
+ }
+
+ methodClassDef = methodClassDef.getSuperclass();
+ } while (methodClassDef != null);
+ return null;
+ }
+
+ private static boolean checkClassAccess(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass) {
+ return definingClass.isPublic() ||
+ getPackage(accessingClass.getClassType()).equals(getPackage(definingClass.getClassType()));
+ }
+
+ private static String getPackage(String classRef) {
+ int lastSlash = classRef.lastIndexOf('/');
+ if (lastSlash < 0) {
+ return "";
+ }
+ return classRef.substring(1, lastSlash);
+ }
+
+ /**
+ *
+ * @param accessingClass The class that contains the field reference. I.e. the class being deodexed
+ * @param instanceClass The inferred class type of the object that the field is being accessed on
+ * @param field The field being accessed
+ * @return The FieldIdItem of the resolved field
+ */
+ private FieldIdItem parseAndResolveField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
+ ClassPath.FieldDef field) {
+ String definingClass = field.definingClass;
+ String fieldName = field.name;
+ String fieldType = field.type;
+
+ StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
+ if (fieldNameItem == null) {
+ return null;
+ }
+
+ TypeIdItem fieldTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldType);
+ if (fieldTypeItem == null) {
+ return null;
+ }
+
+ ClassPath.ClassDef fieldClass = instanceClass;
+
+ ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
+ parents.add(fieldClass);
+
+ while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) {
+ fieldClass = fieldClass.getSuperclass();
+ parents.add(fieldClass);
+ }
+
+ for (int i=parents.size()-1; i>=0; i--) {
+ fieldClass = parents.get(i);
+
+ TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
+ if (classTypeItem == null) {
+ continue;
+ }
+
+ FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
+ if (fieldIdItem != null && checkClassAccess(accessingClass, fieldClass)) {
+ return fieldIdItem;
+ }
+ }
+ return null;
+ }
+
+ public static class InlineMethod {
+ public final int methodType;
+ public final String classType;
+ public final String methodName;
+ public final String parameters;
+ public final String returnType;
+
+ private MethodIdItem methodIdItem = null;
+
+ InlineMethod(int methodType, String classType, String methodName, String parameters,
+ String returnType) {
+ this.methodType = methodType;
+ this.classType = classType;
+ this.methodName = methodName;
+ this.parameters = parameters;
+ this.returnType = returnType;
+ }
+
+ public MethodIdItem getMethodIdItem(DeodexUtil deodexUtil) {
+ if (methodIdItem == null) {
+ loadMethod(deodexUtil);
+ }
+ return methodIdItem;
+ }
+
+ private void loadMethod(DeodexUtil deodexUtil) {
+ ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
+
+ this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters,
+ returnType);
+ }
+
+ public String getMethodString() {
+ return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java
new file mode 100644
index 0000000..ca44268
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java
@@ -0,0 +1,56 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.ClassDefItem;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.TypeIdItem;
+
+import java.util.HashMap;
+
+/**
+ * Keeps a simple map of classes defined in a dex file, allowing you to look them up by TypeIdItem or name
+ */
+public class DexFileClassMap {
+ private final HashMap<String, ClassDefItem> definedClasses = new HashMap<String, ClassDefItem>();
+
+ public DexFileClassMap(DexFile dexFile) {
+ for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
+ definedClasses.put(classDefItem.getClassType().getTypeDescriptor(), classDefItem);
+ }
+ }
+
+ public ClassDefItem getClassDefByName(String typeName) {
+ return definedClasses.get(typeName);
+ }
+
+ public ClassDefItem getClassDefByType(TypeIdItem typeIdItem) {
+ return definedClasses.get(typeIdItem.getTypeDescriptor());
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java
new file mode 100644
index 0000000..a14a667
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java
@@ -0,0 +1,187 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.Code.OdexedInvokeInline;
+import org.jf.dexlib.Code.OdexedInvokeVirtual;
+
+import static org.jf.dexlib.Code.Analysis.DeodexUtil.Static;
+import static org.jf.dexlib.Code.Analysis.DeodexUtil.Virtual;
+import static org.jf.dexlib.Code.Analysis.DeodexUtil.Direct;
+
+public abstract class InlineMethodResolver {
+ public static InlineMethodResolver createInlineMethodResolver(DeodexUtil deodexUtil, int odexVersion) {
+ if (odexVersion == 35) {
+ return new InlineMethodResolver_version35(deodexUtil);
+ } else if (odexVersion == 36) {
+ return new InlineMethodResolver_version36(deodexUtil);
+ } else {
+ throw new RuntimeException(String.format("odex version %d is not supported yet", odexVersion));
+ }
+ }
+
+ protected InlineMethodResolver() {
+ }
+
+ public abstract DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction instruction);
+
+ private static class InlineMethodResolver_version35 extends InlineMethodResolver
+ {
+ private final DeodexUtil.InlineMethod[] inlineMethods;
+
+ public InlineMethodResolver_version35(DeodexUtil deodexUtil) {
+ inlineMethods = new DeodexUtil.InlineMethod[] {
+ new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D")
+ };
+ }
+
+ @Override
+ public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) {
+ assert analyzedInstruction.instruction instanceof OdexedInvokeInline;
+
+ OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction;
+ int inlineIndex = instruction.getInlineIndex();
+
+ if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) {
+ throw new RuntimeException("Invalid inline index: " + inlineIndex);
+ }
+ return inlineMethods[inlineIndex];
+ }
+ }
+
+ private static class InlineMethodResolver_version36 extends InlineMethodResolver
+ {
+ private final DeodexUtil.InlineMethod[] inlineMethods;
+ private final DeodexUtil.InlineMethod indexOfIMethod;
+ private final DeodexUtil.InlineMethod indexOfIIMethod;
+ private final DeodexUtil.InlineMethod fastIndexOfMethod;
+ private final DeodexUtil.InlineMethod isEmptyMethod;
+
+
+ public InlineMethodResolver_version36(DeodexUtil deodexUtil) {
+ //The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being
+ //passed to distinguish between them.
+
+ //froyo
+ indexOfIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I");
+ indexOfIIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I");
+
+ //gingerbread
+ fastIndexOfMethod = new DeodexUtil.InlineMethod(Direct, "Ljava/lang/String;", "fastIndexOf", "II", "I");
+ isEmptyMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z");
+
+ inlineMethods = new DeodexUtil.InlineMethod[] {
+ new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"),
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"),
+ //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"),
+ //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "fastIndexOf", "II", "I"),
+ null,
+ //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"),
+ //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"),
+ null,
+ new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToIntBits", "F", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToRawIntBits", "F", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "intBitsToFloat", "I", "F"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToLongBits", "D", "J"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToRawLongBits", "D", "J"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "longBitsToDouble", "J", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "I", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "J", "J"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "F", "F"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "abs", "D", "D"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "min", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "max", "II", "I"),
+ new DeodexUtil.InlineMethod(Static, "Ljava/lang/StrictMath;", "sqrt", "D", "D"),
+ };
+ }
+
+ @Override
+ public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) {
+ assert analyzedInstruction.instruction instanceof OdexedInvokeInline;
+
+ OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction;
+ int inlineIndex = instruction.getInlineIndex();
+
+ if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) {
+ throw new RuntimeException("Invalid method index: " + inlineIndex);
+ }
+
+ if (inlineIndex == 4) {
+ int parameterCount = getParameterCount(instruction);
+ if (parameterCount == 2) {
+ return indexOfIMethod;
+ } else if (parameterCount == 3) {
+ return fastIndexOfMethod;
+ } else {
+ throw new RuntimeException("Could not determine the correct inline method to use");
+ }
+ } else if (inlineIndex == 5) {
+ int parameterCount = getParameterCount(instruction);
+ if (parameterCount == 3) {
+ return indexOfIIMethod;
+ } else if (parameterCount == 1) {
+ return isEmptyMethod;
+ } else {
+ throw new RuntimeException("Could not determine the correct inline method to use");
+ }
+ }
+
+ return inlineMethods[inlineIndex];
+ }
+
+ private int getParameterCount(OdexedInvokeInline instruction) {
+ return instruction.getRegCount();
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
new file mode 100644
index 0000000..9edf37b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
@@ -0,0 +1,3861 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.*;
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.Code.Format.*;
+import org.jf.dexlib.Util.AccessFlags;
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.SparseArray;
+
+import java.util.BitSet;
+import java.util.EnumSet;
+import java.util.List;
+
+/**
+ * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types
+ * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification
+ * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and
+ * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then
+ * verify it.
+ *
+ * Before calling the analyze() method, you must have initialized the ClassPath by calling
+ * ClassPath.InitializeClassPath
+ */
+public class MethodAnalyzer {
+ private final ClassDataItem.EncodedMethod encodedMethod;
+
+ private final DeodexUtil deodexUtil;
+
+ private SparseArray<AnalyzedInstruction> instructions;
+
+ private static final int NOT_ANALYZED = 0;
+ private static final int ANALYZED = 1;
+ private static final int VERIFIED = 2;
+ private int analyzerState = NOT_ANALYZED;
+
+ private BitSet analyzedInstructions;
+
+ private ValidationException validationException = null;
+
+ //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
+ //register types for this instruction to the parameter types, in order to have them propagate to all of its
+ //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
+ //instruction, etc.
+ private AnalyzedInstruction startOfMethod;
+
+ public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex,
+ InlineMethodResolver inlineResolver) {
+ if (encodedMethod == null) {
+ throw new IllegalArgumentException("encodedMethod cannot be null");
+ }
+ if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) {
+ throw new IllegalArgumentException("The method has no code");
+ }
+ this.encodedMethod = encodedMethod;
+
+ if (deodex) {
+ if (inlineResolver != null) {
+ this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile(), inlineResolver);
+ } else {
+ this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile());
+ }
+ } else {
+ this.deodexUtil = null;
+ }
+
+ //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
+ //have to handle the case this special case of instruction being null, in the main class
+ startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) {
+ public boolean setsRegister() {
+ return false;
+ }
+
+ @Override
+ public boolean setsWideRegister() {
+ return false;
+ }
+
+ @Override
+ public boolean setsRegister(int registerNumber) {
+ return false;
+ }
+
+ @Override
+ public int getDestinationRegister() {
+ assert false;
+ return -1;
+ };
+ };
+
+ buildInstructionList();
+
+ analyzedInstructions = new BitSet(instructions.size());
+ }
+
+ public boolean isAnalyzed() {
+ return analyzerState >= ANALYZED;
+ }
+
+ public boolean isVerified() {
+ return analyzerState == VERIFIED;
+ }
+
+ public void analyze() {
+ assert encodedMethod != null;
+ assert encodedMethod.codeItem != null;
+
+ if (analyzerState >= ANALYZED) {
+ //the instructions have already been analyzed, so there is nothing to do
+ return;
+ }
+
+ CodeItem codeItem = encodedMethod.codeItem;
+ MethodIdItem methodIdItem = encodedMethod.method;
+
+ int totalRegisters = codeItem.getRegisterCount();
+ int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
+
+ int nonParameterRegisters = totalRegisters - parameterRegisters;
+
+ for (AnalyzedInstruction instruction: instructions.getValues()) {
+ instruction.dead = true;
+ }
+
+ //if this isn't a static method, determine which register is the "this" register and set the type to the
+ //current class
+ if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
+ nonParameterRegisters--;
+ int thisRegister = totalRegisters - parameterRegisters - 1;
+
+ //if this is a constructor, then set the "this" register to an uninitialized reference of the current class
+ if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
+ setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
+ RegisterType.getRegisterType(RegisterType.Category.UninitThis,
+ ClassPath.getClassDef(methodIdItem.getContainingClass())));
+ } else {
+ setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
+ RegisterType.getRegisterType(RegisterType.Category.Reference,
+ ClassPath.getClassDef(methodIdItem.getContainingClass())));
+ }
+ }
+
+ TypeListItem parameters = methodIdItem.getPrototype().getParameters();
+ if (parameters != null) {
+ RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
+ for (int i=0; i<parameterTypes.length; i++) {
+ RegisterType registerType = parameterTypes[i];
+ int registerNum = (totalRegisters - parameterRegisters) + i;
+ setPostRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType);
+ }
+ }
+
+ RegisterType uninit = RegisterType.getRegisterType(RegisterType.Category.Uninit, null);
+ for (int i=0; i<nonParameterRegisters; i++) {
+ setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit);
+ }
+
+ BitSet instructionsToAnalyze = new BitSet(instructions.size());
+
+ //make sure all of the "first instructions" are marked for processing
+ for (AnalyzedInstruction successor: startOfMethod.successors) {
+ instructionsToAnalyze.set(successor.instructionIndex);
+ }
+
+ BitSet undeodexedInstructions = new BitSet(instructions.size());
+
+ do {
+ boolean didSomething = false;
+
+ while (!instructionsToAnalyze.isEmpty()) {
+ for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
+ instructionsToAnalyze.clear(i);
+ if (analyzedInstructions.get(i)) {
+ continue;
+ }
+ AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i);
+ instructionToAnalyze.dead = false;
+ try {
+ if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) {
+ //if we had deodexed an odex instruction in a previous pass, we might have more specific
+ //register information now, so let's restore the original odexed instruction and
+ //re-deodex it
+ instructionToAnalyze.restoreOdexedInstruction();
+ }
+
+ if (!analyzeInstruction(instructionToAnalyze)) {
+ undeodexedInstructions.set(i);
+ continue;
+ } else {
+ didSomething = true;
+ undeodexedInstructions.clear(i);
+ }
+ } catch (ValidationException ex) {
+ this.validationException = ex;
+ int codeAddress = getInstructionAddress(instructionToAnalyze);
+ ex.setCodeAddress(codeAddress);
+ ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name));
+ ex.addContext(String.format("CodeAddress: %d", codeAddress));
+ ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
+ break;
+ }
+
+ analyzedInstructions.set(instructionToAnalyze.getInstructionIndex());
+
+ for (AnalyzedInstruction successor: instructionToAnalyze.successors) {
+ instructionsToAnalyze.set(successor.getInstructionIndex());
+ }
+ }
+ if (validationException != null) {
+ break;
+ }
+ }
+
+ if (!didSomething) {
+ break;
+ }
+
+ if (!undeodexedInstructions.isEmpty()) {
+ for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) {
+ instructionsToAnalyze.set(i);
+ }
+ }
+ } while (true);
+
+ //Now, go through and fix up any unresolvable odex instructions. These are usually odex instructions
+ //that operate on a null register, and thus always throw an NPE. They can also be any sort of odex instruction
+ //that occurs after an unresolvable odex instruction. We deodex if possible, or replace with an
+ //UnresolvableOdexInstruction
+ for (int i=0; i<instructions.size(); i++) {
+ AnalyzedInstruction analyzedInstruction = instructions.valueAt(i);
+
+ Instruction instruction = analyzedInstruction.getInstruction();
+
+ if (instruction.opcode.odexOnly()) {
+ int objectRegisterNumber;
+ switch (instruction.getFormat()) {
+ case Format10x:
+ analyzeReturnVoidBarrier(analyzedInstruction, false);
+ continue;
+ case Format21c:
+ case Format22c:
+ analyzePutGetVolatile(analyzedInstruction, false);
+ continue;
+ case Format35c:
+ analyzeInvokeDirectEmpty(analyzedInstruction, false);
+ continue;
+ case Format3rc:
+ analyzeInvokeObjectInitRange(analyzedInstruction, false);
+ continue;
+ case Format22cs:
+ objectRegisterNumber = ((Instruction22cs)instruction).getRegisterB();
+ break;
+ case Format35mi:
+ case Format35ms:
+ objectRegisterNumber = ((FiveRegisterInstruction)instruction).getRegisterD();
+ break;
+ case Format3rmi:
+ case Format3rms:
+ objectRegisterNumber = ((RegisterRangeInstruction)instruction).getStartRegister();
+ break;
+ default:
+ continue;
+ }
+
+ analyzedInstruction.setDeodexedInstruction(new UnresolvedOdexInstruction(instruction, objectRegisterNumber));
+ }
+ }
+
+ analyzerState = ANALYZED;
+ }
+
+ public void verify() {
+ if (analyzerState < ANALYZED) {
+ throw new ExceptionWithContext("You must call analyze() before calling verify().");
+ }
+
+ if (analyzerState == VERIFIED) {
+ //we've already verified the bytecode. nothing to do
+ return;
+ }
+
+ BitSet instructionsToVerify = new BitSet(instructions.size());
+ BitSet verifiedInstructions = new BitSet(instructions.size());
+
+ //make sure all of the "first instructions" are marked for processing
+ for (AnalyzedInstruction successor: startOfMethod.successors) {
+ instructionsToVerify.set(successor.instructionIndex);
+ }
+
+ while (!instructionsToVerify.isEmpty()) {
+ for (int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
+ instructionsToVerify.clear(i);
+ if (verifiedInstructions.get(i)) {
+ continue;
+ }
+ AnalyzedInstruction instructionToVerify = instructions.valueAt(i);
+ try {
+ verifyInstruction(instructionToVerify);
+ } catch (ValidationException ex) {
+ this.validationException = ex;
+ int codeAddress = getInstructionAddress(instructionToVerify);
+ ex.setCodeAddress(codeAddress);
+ ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name));
+ ex.addContext(String.format("CodeAddress: %d", codeAddress));
+ ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
+ break;
+ }
+
+ verifiedInstructions.set(instructionToVerify.getInstructionIndex());
+
+ for (AnalyzedInstruction successor: instructionToVerify.successors) {
+ instructionsToVerify.set(successor.getInstructionIndex());
+ }
+ }
+ if (validationException != null) {
+ break;
+ }
+ }
+
+ analyzerState = VERIFIED;
+ }
+
+ private int getThisRegister() {
+ assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0;
+
+ CodeItem codeItem = encodedMethod.codeItem;
+ assert codeItem != null;
+
+ MethodIdItem methodIdItem = encodedMethod.method;
+ assert methodIdItem != null;
+
+ int totalRegisters = codeItem.getRegisterCount();
+ if (totalRegisters == 0) {
+ throw new ValidationException("A non-static method must have at least 1 register");
+ }
+
+ int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
+
+ return totalRegisters - parameterRegisters - 1;
+ }
+
+ private boolean isInstanceConstructor() {
+ return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 &&
+ (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
+ }
+
+ private boolean isStaticConstructor() {
+ return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 &&
+ (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
+ }
+
+ public AnalyzedInstruction getStartOfMethod() {
+ return startOfMethod;
+ }
+
+ /**
+ * @return a read-only list containing the instructions for tihs method.
+ */
+ public List<AnalyzedInstruction> getInstructions() {
+ return instructions.getValues();
+ }
+
+ public ClassDataItem.EncodedMethod getMethod() {
+ return this.encodedMethod;
+ }
+
+ public ValidationException getValidationException() {
+ return validationException;
+ }
+
+ private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
+ assert typeListItem != null;
+ assert parameterRegisterCount == typeListItem.getRegisterCount();
+
+ RegisterType[] registerTypes = new RegisterType[parameterRegisterCount];
+
+ int registerNum = 0;
+ for (TypeIdItem type: typeListItem.getTypes()) {
+ if (type.getRegisterCount() == 2) {
+ registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
+ registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
+ } else {
+ registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
+ }
+ }
+
+ return registerTypes;
+ }
+
+ public int getInstructionAddress(AnalyzedInstruction instruction) {
+ return instructions.keyAt(instruction.instructionIndex);
+ }
+
+ private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction,
+ RegisterType registerType) {
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
+ registerType);
+ }
+
+ private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber,
+ RegisterType registerType) {
+
+ BitSet changedInstructions = new BitSet(instructions.size());
+
+ if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
+ return;
+ }
+
+ propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions);
+
+ //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
+ //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
+ //the next iteration of the while loop.
+ //This could also be done recursively, but in large methods it would likely cause very deep recursion,
+ //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly
+ //annoying.
+ while (!changedInstructions.isEmpty()) {
+ for (int instructionIndex=changedInstructions.nextSetBit(0);
+ instructionIndex>=0;
+ instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
+
+ changedInstructions.clear(instructionIndex);
+
+ propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber,
+ changedInstructions);
+ }
+ }
+
+ if (registerType.category == RegisterType.Category.LongLo) {
+ checkWidePair(registerNumber, analyzedInstruction);
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
+ RegisterType.getRegisterType(RegisterType.Category.LongHi, null));
+ } else if (registerType.category == RegisterType.Category.DoubleLo) {
+ checkWidePair(registerNumber, analyzedInstruction);
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
+ RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null));
+ }
+ }
+
+ private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber,
+ BitSet changedInstructions) {
+ RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber);
+ for (AnalyzedInstruction successor: instruction.successors) {
+ if (successor.mergeRegister(registerNumber, postRegisterType, analyzedInstructions)) {
+ changedInstructions.set(successor.instructionIndex);
+ }
+ }
+ }
+
+ private void buildInstructionList() {
+ assert encodedMethod != null;
+ assert encodedMethod.codeItem != null;
+ int registerCount = encodedMethod.codeItem.getRegisterCount();
+
+ Instruction[] insns = encodedMethod.codeItem.getInstructions();
+
+ instructions = new SparseArray<AnalyzedInstruction>(insns.length);
+
+ //first, create all the instructions and populate the instructionAddresses array
+ int currentCodeAddress = 0;
+ for (int i=0; i<insns.length; i++) {
+ instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount));
+ assert instructions.indexOfKey(currentCodeAddress) == i;
+ currentCodeAddress += insns[i].getSize(currentCodeAddress);
+ }
+
+ //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
+ //and is covered by a try block should be set to a list of the first instructions of each exception handler
+ //for the try block covering the instruction
+ CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
+ int triesIndex = 0;
+ CodeItem.TryItem currentTry = null;
+ AnalyzedInstruction[] currentExceptionHandlers = null;
+ AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
+
+ if (tries != null) {
+ for (int i=0; i<instructions.size(); i++) {
+ AnalyzedInstruction instruction = instructions.valueAt(i);
+ Opcode instructionOpcode = instruction.instruction.opcode;
+ currentCodeAddress = getInstructionAddress(instruction);
+
+ //check if we have gone past the end of the current try
+ if (currentTry != null) {
+ if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
+ currentTry = null;
+ triesIndex++;
+ }
+ }
+
+ //check if the next try is applicable yet
+ if (currentTry == null && triesIndex < tries.length) {
+ CodeItem.TryItem tryItem = tries[triesIndex];
+ if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
+ assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
+
+ currentTry = tryItem;
+
+ currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
+ }
+ }
+
+ //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
+ //for the current instruction
+ if (currentTry != null && instructionOpcode.canThrow()) {
+ exceptionHandlers[i] = currentExceptionHandlers;
+ }
+ }
+ }
+
+ //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod"
+ //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors,
+ //and no reachable code will have an unreachable predessor or successor
+ assert instructions.size() > 0;
+ BitSet instructionsToProcess = new BitSet(insns.length);
+
+ addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess);
+ while (!instructionsToProcess.isEmpty()) {
+ int currentInstructionIndex = instructionsToProcess.nextSetBit(0);
+ instructionsToProcess.clear(currentInstructionIndex);
+
+ AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex);
+ Opcode instructionOpcode = instruction.instruction.opcode;
+ int instructionCodeAddress = getInstructionAddress(instruction);
+
+ if (instruction.instruction.opcode.canContinue()) {
+ if (instruction.instruction.opcode != Opcode.NOP ||
+ !instruction.instruction.getFormat().variableSizeFormat) {
+
+ if (currentInstructionIndex == instructions.size() - 1) {
+ throw new ValidationException("Execution can continue past the last instruction");
+ }
+
+ AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1);
+ addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess);
+ }
+ }
+
+ if (instruction.instruction instanceof OffsetInstruction) {
+ OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
+
+ if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
+ MultiOffsetInstruction switchDataInstruction =
+ (MultiOffsetInstruction)instructions.get(instructionCodeAddress +
+ offsetInstruction.getTargetAddressOffset()).instruction;
+ for (int targetAddressOffset: switchDataInstruction.getTargets()) {
+ AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
+ targetAddressOffset);
+
+ addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers,
+ instructionsToProcess);
+ }
+ } else {
+ int targetAddressOffset = offsetInstruction.getTargetAddressOffset();
+ AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
+ targetAddressOffset);
+ addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
+ }
+ }
+ }
+ }
+
+ private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
+ AnalyzedInstruction[][] exceptionHandlers,
+ BitSet instructionsToProcess) {
+ addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false);
+ }
+
+ private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
+ AnalyzedInstruction[][] exceptionHandlers,
+ BitSet instructionsToProcess, boolean allowMoveException) {
+
+ if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) {
+ throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name +
+ " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) +
+ " to the move-exception instruction at address 0x" +
+ Integer.toHexString(getInstructionAddress(successor)));
+ }
+
+ if (!successor.addPredecessor(predecessor)) {
+ return;
+ }
+
+ predecessor.addSuccessor(successor);
+ instructionsToProcess.set(successor.getInstructionIndex());
+
+
+ //if the successor can throw an instruction, then we need to add the exception handlers as additional
+ //successors to the predecessor (and then apply this same logic recursively if needed)
+ //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually
+ //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other
+ //instruction. But since it doesn't modify any registers, we can treat it like any other instruction.
+ AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex];
+ if (exceptionHandlersForSuccessor != null) {
+ //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
+ //can throw an exception
+ assert successor.instruction.opcode.canThrow();
+
+ for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
+ addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true);
+ }
+ }
+ }
+
+ private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) {
+ int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length;
+ int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
+ if (catchAllHandler != -1) {
+ exceptionHandlerCount++;
+ }
+
+ AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount];
+ for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) {
+ exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress());
+ }
+
+ if (catchAllHandler != -1) {
+ exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler);
+ }
+
+ return exceptionHandlers;
+ }
+
+ /**
+ * @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its
+ * object register being null
+ */
+ private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) {
+ Instruction instruction = analyzedInstruction.instruction;
+
+ switch (instruction.opcode) {
+ case NOP:
+ return true;
+ case MOVE:
+ case MOVE_FROM16:
+ case MOVE_16:
+ case MOVE_WIDE:
+ case MOVE_WIDE_FROM16:
+ case MOVE_WIDE_16:
+ case MOVE_OBJECT:
+ case MOVE_OBJECT_FROM16:
+ case MOVE_OBJECT_16:
+ analyzeMove(analyzedInstruction);
+ return true;
+ case MOVE_RESULT:
+ case MOVE_RESULT_WIDE:
+ case MOVE_RESULT_OBJECT:
+ analyzeMoveResult(analyzedInstruction);
+ return true;
+ case MOVE_EXCEPTION:
+ analyzeMoveException(analyzedInstruction);
+ return true;
+ case RETURN_VOID:
+ case RETURN:
+ case RETURN_WIDE:
+ case RETURN_OBJECT:
+ return true;
+ case RETURN_VOID_BARRIER:
+ analyzeReturnVoidBarrier(analyzedInstruction);
+ return true;
+ case CONST_4:
+ case CONST_16:
+ case CONST:
+ analyzeConst(analyzedInstruction);
+ return true;
+ case CONST_HIGH16:
+ analyzeConstHigh16(analyzedInstruction);
+ return true;
+ case CONST_WIDE_16:
+ case CONST_WIDE_32:
+ case CONST_WIDE:
+ case CONST_WIDE_HIGH16:
+ analyzeWideConst(analyzedInstruction);
+ return true;
+ case CONST_STRING:
+ case CONST_STRING_JUMBO:
+ analyzeConstString(analyzedInstruction);
+ return true;
+ case CONST_CLASS:
+ case CONST_CLASS_JUMBO:
+ analyzeConstClass(analyzedInstruction);
+ return true;
+ case MONITOR_ENTER:
+ case MONITOR_EXIT:
+ return true;
+ case CHECK_CAST:
+ case CHECK_CAST_JUMBO:
+ analyzeCheckCast(analyzedInstruction);
+ return true;
+ case INSTANCE_OF:
+ case INSTANCE_OF_JUMBO:
+ analyzeInstanceOf(analyzedInstruction);
+ return true;
+ case ARRAY_LENGTH:
+ analyzeArrayLength(analyzedInstruction);
+ return true;
+ case NEW_INSTANCE:
+ case NEW_INSTANCE_JUMBO:
+ analyzeNewInstance(analyzedInstruction);
+ return true;
+ case NEW_ARRAY:
+ case NEW_ARRAY_JUMBO:
+ analyzeNewArray(analyzedInstruction);
+ return true;
+ case FILLED_NEW_ARRAY:
+ case FILLED_NEW_ARRAY_RANGE:
+ case FILLED_NEW_ARRAY_JUMBO:
+ return true;
+ case FILL_ARRAY_DATA:
+ analyzeArrayDataOrSwitch(analyzedInstruction);
+ case THROW:
+ case GOTO:
+ case GOTO_16:
+ case GOTO_32:
+ return true;
+ case PACKED_SWITCH:
+ case SPARSE_SWITCH:
+ analyzeArrayDataOrSwitch(analyzedInstruction);
+ return true;
+ case CMPL_FLOAT:
+ case CMPG_FLOAT:
+ case CMPL_DOUBLE:
+ case CMPG_DOUBLE:
+ case CMP_LONG:
+ analyzeFloatWideCmp(analyzedInstruction);
+ return true;
+ case IF_EQ:
+ case IF_NE:
+ case IF_LT:
+ case IF_GE:
+ case IF_GT:
+ case IF_LE:
+ case IF_EQZ:
+ case IF_NEZ:
+ case IF_LTZ:
+ case IF_GEZ:
+ case IF_GTZ:
+ case IF_LEZ:
+ return true;
+ case AGET:
+ analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case AGET_BOOLEAN:
+ analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
+ return true;
+ case AGET_BYTE:
+ analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
+ return true;
+ case AGET_CHAR:
+ analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
+ return true;
+ case AGET_SHORT:
+ analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
+ return true;
+ case AGET_WIDE:
+ analyzeAgetWide(analyzedInstruction);
+ return true;
+ case AGET_OBJECT:
+ analyzeAgetObject(analyzedInstruction);
+ return true;
+ case APUT:
+ case APUT_BOOLEAN:
+ case APUT_BYTE:
+ case APUT_CHAR:
+ case APUT_SHORT:
+ case APUT_WIDE:
+ case APUT_OBJECT:
+ return true;
+ case IGET:
+ case IGET_JUMBO:
+ analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case IGET_BOOLEAN:
+ case IGET_BOOLEAN_JUMBO:
+ analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
+ return true;
+ case IGET_BYTE:
+ case IGET_BYTE_JUMBO:
+ analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
+ return true;
+ case IGET_CHAR:
+ case IGET_CHAR_JUMBO:
+ analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
+ return true;
+ case IGET_SHORT:
+ case IGET_SHORT_JUMBO:
+ analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
+ return true;
+ case IGET_WIDE:
+ case IGET_WIDE_JUMBO:
+ case IGET_OBJECT:
+ case IGET_OBJECT_JUMBO:
+ analyzeIgetWideObject(analyzedInstruction);
+ return true;
+ case IPUT:
+ case IPUT_JUMBO:
+ case IPUT_BOOLEAN:
+ case IPUT_BOOLEAN_JUMBO:
+ case IPUT_BYTE:
+ case IPUT_BYTE_JUMBO:
+ case IPUT_CHAR:
+ case IPUT_CHAR_JUMBO:
+ case IPUT_SHORT:
+ case IPUT_SHORT_JUMBO:
+ case IPUT_WIDE:
+ case IPUT_WIDE_JUMBO:
+ case IPUT_OBJECT:
+ case IPUT_OBJECT_JUMBO:
+ return true;
+ case SGET:
+ case SGET_JUMBO:
+ analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case SGET_BOOLEAN:
+ case SGET_BOOLEAN_JUMBO:
+ analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
+ return true;
+ case SGET_BYTE:
+ case SGET_BYTE_JUMBO:
+ analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
+ return true;
+ case SGET_CHAR:
+ case SGET_CHAR_JUMBO:
+ analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
+ return true;
+ case SGET_SHORT:
+ case SGET_SHORT_JUMBO:
+ analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
+ return true;
+ case SGET_WIDE:
+ case SGET_WIDE_JUMBO:
+ case SGET_OBJECT:
+ case SGET_OBJECT_JUMBO:
+ analyzeSgetWideObject(analyzedInstruction);
+ return true;
+ case SPUT:
+ case SPUT_JUMBO:
+ case SPUT_BOOLEAN:
+ case SPUT_BOOLEAN_JUMBO:
+ case SPUT_BYTE:
+ case SPUT_BYTE_JUMBO:
+ case SPUT_CHAR:
+ case SPUT_CHAR_JUMBO:
+ case SPUT_SHORT:
+ case SPUT_SHORT_JUMBO:
+ case SPUT_WIDE:
+ case SPUT_WIDE_JUMBO:
+ case SPUT_OBJECT:
+ case SPUT_OBJECT_JUMBO:
+ return true;
+ case INVOKE_VIRTUAL:
+ case INVOKE_SUPER:
+ return true;
+ case INVOKE_DIRECT:
+ analyzeInvokeDirect(analyzedInstruction);
+ return true;
+ case INVOKE_STATIC:
+ case INVOKE_INTERFACE:
+ case INVOKE_VIRTUAL_RANGE:
+ case INVOKE_VIRTUAL_JUMBO:
+ case INVOKE_SUPER_RANGE:
+ case INVOKE_SUPER_JUMBO:
+ return true;
+ case INVOKE_DIRECT_RANGE:
+ case INVOKE_DIRECT_JUMBO:
+ analyzeInvokeDirectRange(analyzedInstruction);
+ return true;
+ case INVOKE_STATIC_RANGE:
+ case INVOKE_STATIC_JUMBO:
+ case INVOKE_INTERFACE_RANGE:
+ case INVOKE_INTERFACE_JUMBO:
+ return true;
+ case NEG_INT:
+ case NOT_INT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case NEG_LONG:
+ case NOT_LONG:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
+ return true;
+ case NEG_FLOAT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
+ return true;
+ case NEG_DOUBLE:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
+ return true;
+ case INT_TO_LONG:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
+ return true;
+ case INT_TO_FLOAT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
+ return true;
+ case INT_TO_DOUBLE:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
+ return true;
+ case LONG_TO_INT:
+ case DOUBLE_TO_INT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case LONG_TO_FLOAT:
+ case DOUBLE_TO_FLOAT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
+ return true;
+ case LONG_TO_DOUBLE:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
+ return true;
+ case FLOAT_TO_INT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
+ return true;
+ case FLOAT_TO_LONG:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
+ return true;
+ case FLOAT_TO_DOUBLE:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
+ return true;
+ case DOUBLE_TO_LONG:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
+ return true;
+ case INT_TO_BYTE:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Byte);
+ return true;
+ case INT_TO_CHAR:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Char);
+ return true;
+ case INT_TO_SHORT:
+ analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Short);
+ return true;
+ case ADD_INT:
+ case SUB_INT:
+ case MUL_INT:
+ case DIV_INT:
+ case REM_INT:
+ case SHL_INT:
+ case SHR_INT:
+ case USHR_INT:
+ analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
+ return true;
+ case AND_INT:
+ case OR_INT:
+ case XOR_INT:
+ analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
+ return true;
+ case ADD_LONG:
+ case SUB_LONG:
+ case MUL_LONG:
+ case DIV_LONG:
+ case REM_LONG:
+ case AND_LONG:
+ case OR_LONG:
+ case XOR_LONG:
+ case SHL_LONG:
+ case SHR_LONG:
+ case USHR_LONG:
+ analyzeBinaryOp(analyzedInstruction, RegisterType.Category.LongLo, false);
+ return true;
+ case ADD_FLOAT:
+ case SUB_FLOAT:
+ case MUL_FLOAT:
+ case DIV_FLOAT:
+ case REM_FLOAT:
+ analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Float, false);
+ return true;
+ case ADD_DOUBLE:
+ case SUB_DOUBLE:
+ case MUL_DOUBLE:
+ case DIV_DOUBLE:
+ case REM_DOUBLE:
+ analyzeBinaryOp(analyzedInstruction, RegisterType.Category.DoubleLo, false);
+ return true;
+ case ADD_INT_2ADDR:
+ case SUB_INT_2ADDR:
+ case MUL_INT_2ADDR:
+ case DIV_INT_2ADDR:
+ case REM_INT_2ADDR:
+ case SHL_INT_2ADDR:
+ case SHR_INT_2ADDR:
+ case USHR_INT_2ADDR:
+ analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, false);
+ return true;
+ case AND_INT_2ADDR:
+ case OR_INT_2ADDR:
+ case XOR_INT_2ADDR:
+ analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, true);
+ return true;
+ case ADD_LONG_2ADDR:
+ case SUB_LONG_2ADDR:
+ case MUL_LONG_2ADDR:
+ case DIV_LONG_2ADDR:
+ case REM_LONG_2ADDR:
+ case AND_LONG_2ADDR:
+ case OR_LONG_2ADDR:
+ case XOR_LONG_2ADDR:
+ case SHL_LONG_2ADDR:
+ case SHR_LONG_2ADDR:
+ case USHR_LONG_2ADDR:
+ analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.LongLo, false);
+ return true;
+ case ADD_FLOAT_2ADDR:
+ case SUB_FLOAT_2ADDR:
+ case MUL_FLOAT_2ADDR:
+ case DIV_FLOAT_2ADDR:
+ case REM_FLOAT_2ADDR:
+ analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Float, false);
+ return true;
+ case ADD_DOUBLE_2ADDR:
+ case SUB_DOUBLE_2ADDR:
+ case MUL_DOUBLE_2ADDR:
+ case DIV_DOUBLE_2ADDR:
+ case REM_DOUBLE_2ADDR:
+ analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.DoubleLo, false);
+ return true;
+ case ADD_INT_LIT16:
+ case RSUB_INT:
+ case MUL_INT_LIT16:
+ case DIV_INT_LIT16:
+ case REM_INT_LIT16:
+ analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
+ return true;
+ case AND_INT_LIT16:
+ case OR_INT_LIT16:
+ case XOR_INT_LIT16:
+ analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
+ return true;
+ case ADD_INT_LIT8:
+ case RSUB_INT_LIT8:
+ case MUL_INT_LIT8:
+ case DIV_INT_LIT8:
+ case REM_INT_LIT8:
+ case SHL_INT_LIT8:
+ analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
+ return true;
+ case AND_INT_LIT8:
+ case OR_INT_LIT8:
+ case XOR_INT_LIT8:
+ analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
+ return true;
+ case SHR_INT_LIT8:
+ analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, true),
+ false);
+ return true;
+ case USHR_INT_LIT8:
+ analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, false),
+ false);
+ return true;
+
+ /*odexed instructions*/
+ case IGET_VOLATILE:
+ case IPUT_VOLATILE:
+ case SGET_VOLATILE:
+ case SPUT_VOLATILE:
+ case IGET_OBJECT_VOLATILE:
+ case IGET_WIDE_VOLATILE:
+ case IPUT_WIDE_VOLATILE:
+ case SGET_WIDE_VOLATILE:
+ case SPUT_WIDE_VOLATILE:
+ analyzePutGetVolatile(analyzedInstruction);
+ return true;
+ case THROW_VERIFICATION_ERROR:
+ return true;
+ case EXECUTE_INLINE:
+ analyzeExecuteInline(analyzedInstruction);
+ return true;
+ case EXECUTE_INLINE_RANGE:
+ analyzeExecuteInlineRange(analyzedInstruction);
+ return true;
+ case INVOKE_DIRECT_EMPTY:
+ analyzeInvokeDirectEmpty(analyzedInstruction);
+ return true;
+ case INVOKE_OBJECT_INIT_RANGE:
+ analyzeInvokeObjectInitRange(analyzedInstruction);
+ return true;
+ case IGET_QUICK:
+ case IGET_WIDE_QUICK:
+ case IGET_OBJECT_QUICK:
+ case IPUT_QUICK:
+ case IPUT_WIDE_QUICK:
+ case IPUT_OBJECT_QUICK:
+ return analyzeIputIgetQuick(analyzedInstruction);
+ case INVOKE_VIRTUAL_QUICK:
+ return analyzeInvokeVirtualQuick(analyzedInstruction, false, false);
+ case INVOKE_SUPER_QUICK:
+ return analyzeInvokeVirtualQuick(analyzedInstruction, true, false);
+ case INVOKE_VIRTUAL_QUICK_RANGE:
+ return analyzeInvokeVirtualQuick(analyzedInstruction, false, true);
+ case INVOKE_SUPER_QUICK_RANGE:
+ return analyzeInvokeVirtualQuick(analyzedInstruction, true, true);
+ case IPUT_OBJECT_VOLATILE:
+ case SGET_OBJECT_VOLATILE:
+ case SPUT_OBJECT_VOLATILE:
+ analyzePutGetVolatile(analyzedInstruction);
+ return true;
+ case INVOKE_OBJECT_INIT_JUMBO:
+ analyzeInvokeObjectInitJumbo(analyzedInstruction);
+ return true;
+ case IGET_VOLATILE_JUMBO:
+ case IGET_WIDE_VOLATILE_JUMBO:
+ case IGET_OBJECT_VOLATILE_JUMBO:
+ case IPUT_VOLATILE_JUMBO:
+ case IPUT_WIDE_VOLATILE_JUMBO:
+ case IPUT_OBJECT_VOLATILE_JUMBO:
+ case SGET_VOLATILE_JUMBO:
+ case SGET_WIDE_VOLATILE_JUMBO:
+ case SGET_OBJECT_VOLATILE_JUMBO:
+ case SPUT_VOLATILE_JUMBO:
+ case SPUT_WIDE_VOLATILE_JUMBO:
+ case SPUT_OBJECT_VOLATILE_JUMBO:
+ analyzePutGetVolatile(analyzedInstruction);
+ return true;
+ default:
+ assert false;
+ return true;
+ }
+ }
+
+
+ private void verifyInstruction(AnalyzedInstruction analyzedInstruction) {
+ Instruction instruction = analyzedInstruction.instruction;
+
+ switch (instruction.opcode) {
+ case NOP:
+ return;
+ case MOVE:
+ case MOVE_FROM16:
+ case MOVE_16:
+ verifyMove(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case MOVE_WIDE:
+ case MOVE_WIDE_FROM16:
+ case MOVE_WIDE_16:
+ verifyMove(analyzedInstruction, WideLowCategories);
+ return;
+ case MOVE_OBJECT:
+ case MOVE_OBJECT_FROM16:
+ case MOVE_OBJECT_16:
+ verifyMove(analyzedInstruction, ReferenceOrUninitCategories);
+ return;
+ case MOVE_RESULT:
+ verifyMoveResult(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case MOVE_RESULT_WIDE:
+ verifyMoveResult(analyzedInstruction, WideLowCategories);
+ return;
+ case MOVE_RESULT_OBJECT:
+ verifyMoveResult(analyzedInstruction, ReferenceCategories);
+ return;
+ case MOVE_EXCEPTION:
+ verifyMoveException(analyzedInstruction);
+ return;
+ case RETURN_VOID:
+ case RETURN_VOID_BARRIER:
+ verifyReturnVoid(analyzedInstruction);
+ return;
+ case RETURN:
+ verifyReturn(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case RETURN_WIDE:
+ verifyReturn(analyzedInstruction, WideLowCategories);
+ return;
+ case RETURN_OBJECT:
+ verifyReturn(analyzedInstruction, ReferenceCategories);
+ return;
+ case CONST_4:
+ case CONST_16:
+ case CONST:
+ case CONST_HIGH16:
+ case CONST_WIDE_16:
+ case CONST_WIDE_32:
+ case CONST_WIDE:
+ case CONST_WIDE_HIGH16:
+ case CONST_STRING:
+ case CONST_STRING_JUMBO:
+ return;
+ case CONST_CLASS:
+ case CONST_CLASS_JUMBO:
+ verifyConstClass(analyzedInstruction);
+ return;
+ case MONITOR_ENTER:
+ case MONITOR_EXIT:
+ verifyMonitor(analyzedInstruction);
+ return;
+ case CHECK_CAST:
+ case CHECK_CAST_JUMBO:
+ verifyCheckCast(analyzedInstruction);
+ return;
+ case INSTANCE_OF:
+ case INSTANCE_OF_JUMBO:
+ verifyInstanceOf(analyzedInstruction);
+ return;
+ case ARRAY_LENGTH:
+ verifyArrayLength(analyzedInstruction);
+ return;
+ case NEW_INSTANCE:
+ case NEW_INSTANCE_JUMBO:
+ verifyNewInstance(analyzedInstruction);
+ return;
+ case NEW_ARRAY:
+ verifyNewArray(analyzedInstruction);
+ return;
+ case FILLED_NEW_ARRAY:
+ verifyFilledNewArray(analyzedInstruction);
+ return;
+ case FILLED_NEW_ARRAY_RANGE:
+ verifyFilledNewArrayRange(analyzedInstruction);
+ return;
+ case FILL_ARRAY_DATA:
+ verifyFillArrayData(analyzedInstruction);
+ return;
+ case THROW:
+ verifyThrow(analyzedInstruction);
+ return;
+ case GOTO:
+ case GOTO_16:
+ case GOTO_32:
+ return;
+ case PACKED_SWITCH:
+ verifySwitch(analyzedInstruction, Format.PackedSwitchData);
+ return;
+ case SPARSE_SWITCH:
+ verifySwitch(analyzedInstruction, Format.SparseSwitchData);
+ return;
+ case CMPL_FLOAT:
+ case CMPG_FLOAT:
+ verifyFloatWideCmp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case CMPL_DOUBLE:
+ case CMPG_DOUBLE:
+ case CMP_LONG:
+ verifyFloatWideCmp(analyzedInstruction, WideLowCategories);
+ return;
+ case IF_EQ:
+ case IF_NE:
+ verifyIfEqNe(analyzedInstruction);
+ return;
+ case IF_LT:
+ case IF_GE:
+ case IF_GT:
+ case IF_LE:
+ verifyIf(analyzedInstruction);
+ return;
+ case IF_EQZ:
+ case IF_NEZ:
+ verifyIfEqzNez(analyzedInstruction);
+ return;
+ case IF_LTZ:
+ case IF_GEZ:
+ case IF_GTZ:
+ case IF_LEZ:
+ verifyIfz(analyzedInstruction);
+ return;
+ case AGET:
+ verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case AGET_BOOLEAN:
+ verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case AGET_BYTE:
+ verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case AGET_CHAR:
+ verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case AGET_SHORT:
+ verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case AGET_WIDE:
+ verifyAgetWide(analyzedInstruction);
+ return;
+ case AGET_OBJECT:
+ verifyAgetObject(analyzedInstruction);
+ return;
+ case APUT:
+ verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case APUT_BOOLEAN:
+ verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case APUT_BYTE:
+ verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case APUT_CHAR:
+ verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case APUT_SHORT:
+ verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case APUT_WIDE:
+ verifyAputWide(analyzedInstruction);
+ return;
+ case APUT_OBJECT:
+ verifyAputObject(analyzedInstruction);
+ return;
+ case IGET:
+ verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case IGET_BOOLEAN:
+ verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case IGET_BYTE:
+ verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case IGET_CHAR:
+ verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case IGET_SHORT:
+ verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case IGET_WIDE:
+ verifyIgetWide(analyzedInstruction);
+ return;
+ case IGET_OBJECT:
+ verifyIgetObject(analyzedInstruction);
+ return;
+ case IPUT:
+ verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case IPUT_BOOLEAN:
+ verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case IPUT_BYTE:
+ verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case IPUT_CHAR:
+ verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case IPUT_SHORT:
+ verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case IPUT_WIDE:
+ verifyIputWide(analyzedInstruction);
+ return;
+ case IPUT_OBJECT:
+ verifyIputObject(analyzedInstruction);
+ return;
+ case SGET:
+ verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case SGET_BOOLEAN:
+ verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case SGET_BYTE:
+ verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case SGET_CHAR:
+ verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case SGET_SHORT:
+ verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case SGET_WIDE:
+ verifySgetWide(analyzedInstruction);
+ return;
+ case SGET_OBJECT:
+ verifySgetObject(analyzedInstruction);
+ return;
+ case SPUT:
+ verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Integer);
+ return;
+ case SPUT_BOOLEAN:
+ verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Boolean);
+ return;
+ case SPUT_BYTE:
+ verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Byte);
+ return;
+ case SPUT_CHAR:
+ verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Char);
+ return;
+ case SPUT_SHORT:
+ verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Short);
+ return;
+ case SPUT_WIDE:
+ verifySputWide(analyzedInstruction);
+ return;
+ case SPUT_OBJECT:
+ verifySputObject(analyzedInstruction);
+ return;
+ case INVOKE_VIRTUAL:
+ verifyInvoke(analyzedInstruction, INVOKE_VIRTUAL);
+ return;
+ case INVOKE_SUPER:
+ verifyInvoke(analyzedInstruction, INVOKE_SUPER);
+ return;
+ case INVOKE_DIRECT:
+ verifyInvoke(analyzedInstruction, INVOKE_DIRECT);
+ return;
+ case INVOKE_STATIC:
+ verifyInvoke(analyzedInstruction, INVOKE_STATIC);
+ return;
+ case INVOKE_INTERFACE:
+ verifyInvoke(analyzedInstruction, INVOKE_INTERFACE);
+ return;
+ case INVOKE_VIRTUAL_RANGE:
+ verifyInvokeRange(analyzedInstruction, INVOKE_VIRTUAL);
+ return;
+ case INVOKE_SUPER_RANGE:
+ verifyInvokeRange(analyzedInstruction, INVOKE_SUPER);
+ return;
+ case INVOKE_DIRECT_RANGE:
+ verifyInvokeRange(analyzedInstruction, INVOKE_DIRECT);
+ return;
+ case INVOKE_STATIC_RANGE:
+ verifyInvokeRange(analyzedInstruction, INVOKE_STATIC);
+ return;
+ case INVOKE_INTERFACE_RANGE:
+ verifyInvokeRange(analyzedInstruction, INVOKE_INTERFACE);
+ return;
+ case NEG_INT:
+ case NOT_INT:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case NEG_LONG:
+ case NOT_LONG:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case NEG_FLOAT:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case NEG_DOUBLE:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case INT_TO_LONG:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case INT_TO_FLOAT:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case INT_TO_DOUBLE:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case LONG_TO_INT:
+ case DOUBLE_TO_INT:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case LONG_TO_FLOAT:
+ case DOUBLE_TO_FLOAT:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case LONG_TO_DOUBLE:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case FLOAT_TO_INT:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case FLOAT_TO_LONG:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case FLOAT_TO_DOUBLE:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case DOUBLE_TO_LONG:
+ verifyUnaryOp(analyzedInstruction, WideLowCategories);
+ return;
+ case INT_TO_BYTE:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case INT_TO_CHAR:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case INT_TO_SHORT:
+ verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
+ return;
+ case ADD_INT:
+ case SUB_INT:
+ case MUL_INT:
+ case DIV_INT:
+ case REM_INT:
+ case SHL_INT:
+ case SHR_INT:
+ case USHR_INT:
+ case AND_INT:
+ case OR_INT:
+ case XOR_INT:
+ verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
+ return;
+ case ADD_LONG:
+ case SUB_LONG:
+ case MUL_LONG:
+ case DIV_LONG:
+ case REM_LONG:
+ case AND_LONG:
+ case OR_LONG:
+ case XOR_LONG:
+ verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories);
+ return;
+ case SHL_LONG:
+ case SHR_LONG:
+ case USHR_LONG:
+ verifyBinaryOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories);
+ return;
+ case ADD_FLOAT:
+ case SUB_FLOAT:
+ case MUL_FLOAT:
+ case DIV_FLOAT:
+ case REM_FLOAT:
+ verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
+ return;
+ case ADD_DOUBLE:
+ case SUB_DOUBLE:
+ case MUL_DOUBLE:
+ case DIV_DOUBLE:
+ case REM_DOUBLE:
+ verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories);
+ return;
+ case ADD_INT_2ADDR:
+ case SUB_INT_2ADDR:
+ case MUL_INT_2ADDR:
+ case DIV_INT_2ADDR:
+ case REM_INT_2ADDR:
+ case SHL_INT_2ADDR:
+ case SHR_INT_2ADDR:
+ case USHR_INT_2ADDR:
+ case AND_INT_2ADDR:
+ case OR_INT_2ADDR:
+ case XOR_INT_2ADDR:
+ verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
+ return;
+ case ADD_LONG_2ADDR:
+ case SUB_LONG_2ADDR:
+ case MUL_LONG_2ADDR:
+ case DIV_LONG_2ADDR:
+ case REM_LONG_2ADDR:
+ case AND_LONG_2ADDR:
+ case OR_LONG_2ADDR:
+ case XOR_LONG_2ADDR:
+ verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories);
+ return;
+ case SHL_LONG_2ADDR:
+ case SHR_LONG_2ADDR:
+ case USHR_LONG_2ADDR:
+ verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories);
+ return;
+ case ADD_FLOAT_2ADDR:
+ case SUB_FLOAT_2ADDR:
+ case MUL_FLOAT_2ADDR:
+ case DIV_FLOAT_2ADDR:
+ case REM_FLOAT_2ADDR:
+ verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
+ return;
+ case ADD_DOUBLE_2ADDR:
+ case SUB_DOUBLE_2ADDR:
+ case MUL_DOUBLE_2ADDR:
+ case DIV_DOUBLE_2ADDR:
+ case REM_DOUBLE_2ADDR:
+ verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories);
+ return;
+ case ADD_INT_LIT16:
+ case RSUB_INT:
+ case MUL_INT_LIT16:
+ case DIV_INT_LIT16:
+ case REM_INT_LIT16:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case AND_INT_LIT16:
+ case OR_INT_LIT16:
+ case XOR_INT_LIT16:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case ADD_INT_LIT8:
+ case RSUB_INT_LIT8:
+ case MUL_INT_LIT8:
+ case DIV_INT_LIT8:
+ case REM_INT_LIT8:
+ case SHL_INT_LIT8:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case AND_INT_LIT8:
+ case OR_INT_LIT8:
+ case XOR_INT_LIT8:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case SHR_INT_LIT8:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case USHR_INT_LIT8:
+ verifyLiteralBinaryOp(analyzedInstruction);
+ return;
+ case IGET_VOLATILE:
+ case IPUT_VOLATILE:
+ case SGET_VOLATILE:
+ case SPUT_VOLATILE:
+ case IGET_OBJECT_VOLATILE:
+ case IGET_WIDE_VOLATILE:
+ case IPUT_WIDE_VOLATILE:
+ case SGET_WIDE_VOLATILE:
+ case SPUT_WIDE_VOLATILE:
+ case THROW_VERIFICATION_ERROR:
+ case EXECUTE_INLINE:
+ case EXECUTE_INLINE_RANGE:
+ case INVOKE_DIRECT_EMPTY:
+ case INVOKE_OBJECT_INIT_RANGE:
+ case IGET_QUICK:
+ case IGET_WIDE_QUICK:
+ case IGET_OBJECT_QUICK:
+ case IPUT_QUICK:
+ case IPUT_WIDE_QUICK:
+ case IPUT_OBJECT_QUICK:
+ case INVOKE_VIRTUAL_QUICK:
+ case INVOKE_SUPER_QUICK:
+ case INVOKE_VIRTUAL_QUICK_RANGE:
+ case INVOKE_SUPER_QUICK_RANGE:
+ case IPUT_OBJECT_VOLATILE:
+ case SGET_OBJECT_VOLATILE:
+ case SPUT_OBJECT_VOLATILE:
+ case INVOKE_OBJECT_INIT_JUMBO:
+ case IGET_VOLATILE_JUMBO:
+ case IGET_WIDE_VOLATILE_JUMBO:
+ case IGET_OBJECT_VOLATILE_JUMBO:
+ case IPUT_VOLATILE_JUMBO:
+ case IPUT_WIDE_VOLATILE_JUMBO:
+ case IPUT_OBJECT_VOLATILE_JUMBO:
+ case SGET_VOLATILE_JUMBO:
+ case SGET_WIDE_VOLATILE_JUMBO:
+ case SGET_OBJECT_VOLATILE_JUMBO:
+ case SPUT_VOLATILE_JUMBO:
+ case SPUT_WIDE_VOLATILE_JUMBO:
+ case SPUT_OBJECT_VOLATILE_JUMBO:
+ //TODO: throw validation exception?
+ default:
+ assert false;
+ return;
+ }
+ }
+
+ private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.One,
+ RegisterType.Category.Boolean,
+ RegisterType.Category.Byte,
+ RegisterType.Category.PosByte,
+ RegisterType.Category.Short,
+ RegisterType.Category.PosShort,
+ RegisterType.Category.Char,
+ RegisterType.Category.Integer,
+ RegisterType.Category.Float);
+
+ private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of(
+ RegisterType.Category.LongLo,
+ RegisterType.Category.DoubleLo);
+
+ private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of(
+ RegisterType.Category.LongHi,
+ RegisterType.Category.DoubleHi);
+
+ private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.Reference);
+
+ private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.UninitThis,
+ RegisterType.Category.Reference);
+
+ private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.UninitRef,
+ RegisterType.Category.UninitThis,
+ RegisterType.Category.Reference);
+
+ private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.One,
+ RegisterType.Category.Boolean,
+ RegisterType.Category.Byte,
+ RegisterType.Category.PosByte,
+ RegisterType.Category.Short,
+ RegisterType.Category.PosShort,
+ RegisterType.Category.Char,
+ RegisterType.Category.Integer,
+ RegisterType.Category.Float,
+ RegisterType.Category.Reference);
+
+ private static final EnumSet<RegisterType.Category> BooleanCategories = EnumSet.of(
+ RegisterType.Category.Null,
+ RegisterType.Category.One,
+ RegisterType.Category.Boolean);
+
+ private void analyzeMove(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
+ }
+
+ private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories);
+ }
+
+ private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) {
+ AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
+ if (!previousInstruction.instruction.opcode.setsResult()) {
+ throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
+ "invoke-*/fill-new-array instruction");
+ }
+
+ RegisterType resultRegisterType;
+ InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction;
+ Item item = invokeInstruction.getReferencedItem();
+
+ if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) {
+ resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
+ ((MethodIdItem)item).getPrototype().getReturnType());
+ } else {
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+ resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType);
+ }
+
+ private void verifyMoveResult(AnalyzedInstruction analyzedInstruction,
+ EnumSet<RegisterType.Category> allowedCategories) {
+ if (analyzedInstruction.instructionIndex == 0) {
+ throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " +
+ "instruction in a method. It must occur after an invoke-*/fill-new-array instruction");
+ }
+
+ AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
+
+ if (!previousInstruction.instruction.opcode.setsResult()) {
+ throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
+ "invoke-*/fill-new-array instruction");
+ }
+
+ //TODO: does dalvik allow a move-result after an invoke with a void return type?
+ RegisterType resultRegisterType;
+
+ InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction();
+ Item item = invokeInstruction.getReferencedItem();
+
+ if (item instanceof MethodIdItem) {
+ resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
+ ((MethodIdItem)item).getPrototype().getReturnType());
+ } else {
+ assert item instanceof TypeIdItem;
+ resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ }
+
+ if (!allowedCategories.contains(resultRegisterType.category)) {
+ throw new ValidationException(String.format("Wrong move-result* instruction for return value %s",
+ resultRegisterType.toString()));
+ }
+ }
+
+ private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) {
+ CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
+ int instructionAddress = getInstructionAddress(analyzedInstruction);
+
+ if (tries == null) {
+ throw new ValidationException("move-exception must be the first instruction in an exception handler block");
+ }
+
+ RegisterType exceptionType = null;
+
+ for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
+ if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
+ exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
+ ClassPath.getClassDef("Ljava/lang/Throwable;"));
+ break;
+ }
+ for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
+ if (handler.getHandlerAddress() == instructionAddress) {
+ exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
+ .merge(exceptionType);
+ }
+ }
+ }
+
+ if (exceptionType == null) {
+ throw new ValidationException("move-exception must be the first instruction in an exception handler block");
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
+ }
+
+ private void verifyMoveException(AnalyzedInstruction analyzedInstruction) {
+ CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
+ int instructionAddress = getInstructionAddress(analyzedInstruction);
+
+ if (tries == null) {
+ throw new ValidationException("move-exception must be the first instruction in an exception handler block");
+ }
+
+ RegisterType exceptionType = null;
+
+ for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
+ if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
+ exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
+ ClassPath.getClassDef("Ljava/lang/Throwable;"));
+ break;
+ }
+ for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
+ if (handler.getHandlerAddress() == instructionAddress) {
+ exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
+ .merge(exceptionType);
+ }
+ }
+ }
+
+ if (exceptionType == null) {
+ throw new ValidationException("move-exception must be the first instruction in an exception handler block");
+ }
+
+ //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c)
+ if (exceptionType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Exception type %s is not a reference type",
+ exceptionType.toString()));
+ }
+ }
+
+ private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) {
+ analyzeReturnVoidBarrier(analyzedInstruction, true);
+ }
+
+ private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
+ Instruction10x instruction = (Instruction10x)analyzedInstruction.instruction;
+
+ Instruction10x deodexedInstruction = new Instruction10x(Opcode.RETURN_VOID);
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ if (analyzeResult) {
+ analyzeInstruction(analyzedInstruction);
+ }
+ }
+
+ private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) {
+ TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
+ if (returnType.getTypeDescriptor().charAt(0) != 'V') {
+ //TODO: could add which return-* variation should be used instead
+ throw new ValidationException("Cannot use return-void with a non-void return type (" +
+ returnType.getTypeDescriptor() + ")");
+ }
+ }
+
+ private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
+ /*if (this.isInstanceConstructor()) {
+ checkConstructorReturn(analyzedInstruction);
+ }*/
+
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+ int returnRegister = instruction.getRegisterA();
+ RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister,
+ validCategories);
+
+ TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
+ if (returnType.getTypeDescriptor().charAt(0) == 'V') {
+ throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
+ }
+
+ RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType);
+
+ if (!validCategories.contains(methodReturnRegisterType.category)) {
+ //TODO: could add which return-* variation should be used instead
+ throw new ValidationException(String.format("Cannot use %s with return type %s",
+ analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor()));
+ }
+
+ if (validCategories == ReferenceCategories) {
+ if (methodReturnRegisterType.type.isInterface()) {
+ if (returnRegisterType.category != RegisterType.Category.Null &&
+ !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
+ //TODO: how to handle warnings?
+ }
+ } else {
+ if (returnRegisterType.category == RegisterType.Category.Reference &&
+ !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
+
+ throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
+ "compatible with the method's return type %s", returnRegister,
+ returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
+ }
+ }
+ }
+ }
+
+ private void analyzeConst(AnalyzedInstruction analyzedInstruction) {
+ LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
+
+ RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral());
+
+ //we assume that the literal value is a valid value for the given instruction type, because it's impossible
+ //to store an invalid literal with the instruction. so we don't need to check the type of the literal
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
+ }
+
+ private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) {
+ //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an
+ //integer
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Integer, null));
+ }
+
+ private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
+ }
+
+ private void analyzeConstString(AnalyzedInstruction analyzedInstruction) {
+ ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;");
+ RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef);
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
+ }
+
+ private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) {
+ ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
+ RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
+ }
+
+
+ private void verifyConstClass(AnalyzedInstruction analyzedInstruction) {
+ ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
+ RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
+
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ //TODO: need to check class access
+ //make sure the referenced class is resolvable
+ ClassPath.getClassDef((TypeIdItem)item);
+ }
+
+ private void verifyMonitor(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories);
+ }
+
+ private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
+ }
+
+ private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) {
+ {
+ //ensure the "source" register is a reference type
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
+ ReferenceCategories);
+ }
+
+ {
+ //resolve and verify the class that we're casting to
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ //TODO: need to check class access
+ RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ if (castRegisterType.category != RegisterType.Category.Reference) {
+ //TODO: verify that dalvik allows a non-reference type..
+ //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime)
+ }
+ }
+ }
+
+ private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Boolean, null));
+ }
+
+ private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) {
+ {
+ //ensure the register that is being checks is a reference type
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories);
+ }
+
+ {
+ //resolve and verify the class that we're checking against
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+ RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ if (registerType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s",
+ registerType.toString()));
+ }
+
+ //TODO: is it valid to use an array type?
+ //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect.
+ }
+ }
+
+ private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Integer, null));
+ }
+
+ private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ int arrayRegisterNumber = instruction.getRegisterB();
+ RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber,
+ ReferenceCategories);
+
+ if (arrayRegisterType.type != null) {
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ }
+ }
+
+ private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
+ RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register);
+ if (destRegisterType.category != RegisterType.Category.Unknown) {
+ assert destRegisterType.category == RegisterType.Category.UninitRef;
+
+ //the post-instruction destination register will only be set if we have already analyzed this instruction
+ //at least once. If this is the case, then the uninit reference has already been propagated to all
+ //successors and nothing else needs to be done.
+ return;
+ }
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getUnitializedReference(classType.type));
+ }
+
+ private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
+ RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register];
+ if (destRegisterType.category != RegisterType.Category.Unknown) {
+ assert destRegisterType.category == RegisterType.Category.UninitRef;
+
+ //the "post-instruction" destination register will only be set if we've gone over
+ //this instruction at least once before. If this is the case, then we need to check
+ //all the other registers, and make sure that none of them contain the same
+ //uninitialized reference that is in the destination register.
+
+ for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
+ if (i==register) {
+ continue;
+ }
+
+ if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) {
+ throw new ValidationException(String.format("Register v%d contains an uninitialized reference " +
+ "that was created by this new-instance instruction.", i));
+ }
+ }
+
+ return;
+ }
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ //TODO: need to check class access
+ RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ if (classType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use new-instance with a non-reference type %s",
+ classType.toString()));
+ }
+
+ if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') {
+ throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() +
+ "\" with new-instance. Use new-array instead.");
+ }
+ }
+
+ private void analyzeNewArray(AnalyzedInstruction analyzedInstruction) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ assert arrayType.type instanceof ClassPath.ArrayClassDef;
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
+ }
+
+ private void verifyNewArray(AnalyzedInstruction analyzedInstruction) {
+ {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
+ }
+
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
+ assert arrayType.type instanceof ClassPath.ArrayClassDef;
+
+ if (arrayType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use new-array with a non-reference type %s",
+ arrayType.toString()));
+ }
+ if (arrayType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() +
+ "\" with new-array. Use new-instance instead.");
+ }
+ }
+
+ private void verifyFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction,
+ RegisterIterator registerIterator) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ RegisterType arrayType;
+ RegisterType arrayImmediateElementType;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
+
+ ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
+
+ if (classDef.getClassType().charAt(0) != '[') {
+ throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
+ "\" with new-array. Use new-instance instead.");
+ }
+
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
+ arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
+ arrayImmediateElementType = RegisterType.getRegisterTypeForType(
+ arrayClassDef.getImmediateElementClass().getClassType());
+ String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
+ if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
+ throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
+ "(long or double)");
+ }
+
+ do {
+ int register = registerIterator.getRegister();
+ RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
+ assert elementType != null;
+
+ if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
+ throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
+ elementType.toString() + " and is incompatible with the array type " +
+ arrayType.type.getClassType());
+ }
+ } while (registerIterator.moveNext());
+ }
+
+ private void verifyFilledNewArray(AnalyzedInstruction analyzedInstruction) {
+ FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
+ verifyFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction));
+ }
+
+ private void verifyFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) {
+ RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
+
+ //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually
+ //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together
+ if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) {
+ throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " +
+ "is larger than the largest allowed register of v65535.",
+ instruction.getStartRegister(),
+ instruction.getStartRegister() + instruction.getRegCount() - 1));
+ }
+
+ verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction));
+ }
+
+ private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ int register = instruction.getRegisterA();
+ RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
+ assert registerType != null;
+
+ if (registerType.category == RegisterType.Category.Null) {
+ return;
+ }
+
+ if (registerType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " +
+ "type %s", register, registerType.toString()));
+ }
+
+ assert registerType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type;
+
+ if (arrayClassDef.getArrayDimensions() != 1) {
+ throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " +
+ "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
+ }
+
+ int elementWidth;
+ switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) {
+ case 'Z':
+ case 'B':
+ elementWidth = 1;
+ break;
+ case 'C':
+ case 'S':
+ elementWidth = 2;
+ break;
+ case 'I':
+ case 'F':
+ elementWidth = 4;
+ break;
+ case 'J':
+ case 'D':
+ elementWidth = 8;
+ break;
+ default:
+ throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " +
+ "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
+ }
+
+
+ int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
+ int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset;
+ AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress);
+ if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) {
+ throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x",
+ arrayDataCodeAddress));
+ }
+
+ ArrayDataPseudoInstruction arrayDataPseudoInstruction =
+ (ArrayDataPseudoInstruction)arrayDataInstruction.instruction;
+
+ if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) {
+ throw new ValidationException(String.format("The array data at code address 0x%x does not have the " +
+ "correct element width for array type %s. Expecting element width %d, got element width %d.",
+ arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth,
+ arrayDataPseudoInstruction.getElementWidth()));
+ }
+ }
+
+ private void verifyThrow(AnalyzedInstruction analyzedInstruction) {
+ int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
+
+ RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
+ assert registerType != null;
+
+ if (registerType.category == RegisterType.Category.Null) {
+ return;
+ }
+
+ if (registerType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d",
+ registerType.toString(), register));
+ }
+
+ assert registerType.type != null;
+
+ if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) {
+ throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d",
+ registerType.type.getClassType(), register));
+ }
+ }
+
+ private void analyzeArrayDataOrSwitch(AnalyzedInstruction analyzedInstruction) {
+ int dataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
+
+ int dataCodeAddress = this.getInstructionAddress(analyzedInstruction) + dataAddressOffset;
+ AnalyzedInstruction dataAnalyzedInstruction = instructions.get(dataCodeAddress);
+
+ if (dataAnalyzedInstruction != null) {
+ dataAnalyzedInstruction.dead = false;
+
+ //if there is a preceding nop, it's deadness should be the same
+ AnalyzedInstruction priorInstruction =
+ instructions.valueAt(dataAnalyzedInstruction.getInstructionIndex()-1);
+ if (priorInstruction.getInstruction().opcode == Opcode.NOP &&
+ !priorInstruction.getInstruction().getFormat().variableSizeFormat) {
+
+ priorInstruction.dead = false;
+ }
+ }
+ }
+
+ private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) {
+ int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
+ int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
+
+ getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories);
+
+ int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset;
+ AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress);
+
+ if (switchDataAnalyzedInstruction == null ||
+ switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) {
+ throw new ValidationException(String.format("There is no %s structure at code address 0x%x",
+ expectedSwitchDataFormat.name(), switchDataCodeAddress));
+ }
+ }
+
+ private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Byte, null));
+ }
+
+ private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories);
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories);
+ }
+
+ private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ assert registerType1 != null;
+
+ RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert registerType2 != null;
+
+ if (!(
+ (ReferenceCategories.contains(registerType1.category) &&
+ ReferenceCategories.contains(registerType2.category))
+ ||
+ (Primitive32BitCategories.contains(registerType1.category) &&
+ Primitive32BitCategories.contains(registerType2.category))
+ )) {
+
+ throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " +
+ "%s. They must both be a reference type or a primitive 32 bit type.",
+ analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString()));
+ }
+ }
+
+ private void verifyIf(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
+ }
+
+ private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
+ ReferenceAndPrimitive32BitCategories);
+ }
+
+ private void verifyIfz(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
+ }
+
+ private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(instructionCategory, null));
+ }
+
+ private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with non-array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use %s with non-array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ if (arrayClassDef.getArrayDimensions() != 1) {
+ throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
+ }
+
+ RegisterType arrayBaseType =
+ RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
+ if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
+ if (arrayBaseType == 'J') {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
+ } else if (arrayBaseType == 'D') {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null));
+ } else {
+ throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+ } else {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
+ }
+ }
+
+ private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
+ arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ if (arrayClassDef.getArrayDimensions() != 1) {
+ throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
+ if (arrayBaseType != 'J' && arrayBaseType != 'D') {
+ throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
+ char elementTypePrefix = elementClassDef.getClassType().charAt(0);
+ if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef));
+ } else {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(RegisterType.Category.Null, null));
+ }
+ }
+
+ private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
+ arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
+ char elementTypePrefix = elementClassDef.getClassType().charAt(0);
+ if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+
+ RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ assert sourceRegisterType != null;
+ RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
+ if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
+ throw new ValidationException(String.format("Cannot use %s with source register type %s.",
+ analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
+ }
+
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with non-array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use %s with non-array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ if (arrayClassDef.getArrayDimensions() != 1) {
+ throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
+ analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
+ }
+
+ RegisterType arrayBaseType =
+ RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
+ if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void verifyAputWide(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
+ arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ if (arrayClassDef.getArrayDimensions() != 1) {
+ throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
+ if (arrayBaseType != 'J' && arrayBaseType != 'D') {
+ throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void verifyAputObject(AnalyzedInstruction analyzedInstruction) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
+
+ RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ assert sourceRegisterType != null;
+
+ //TODO: ensure sourceRegisterType is a Reference type?
+
+ RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ assert arrayRegisterType != null;
+
+ if (arrayRegisterType.category != RegisterType.Category.Null) {
+ //don't check the source type against the array type, just make sure it is an array of reference types
+
+ if (arrayRegisterType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
+ arrayRegisterType.category.toString()));
+ }
+
+ assert arrayRegisterType.type != null;
+ if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
+ arrayRegisterType.type.getClassType()));
+ }
+
+ assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
+ ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
+
+ ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
+ char elementTypePrefix = elementClassDef.getClassType().charAt(0);
+ if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
+ throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
+ "array type for the instruction.", arrayRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(instructionCategory, null));
+ }
+
+ private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
+ }
+
+ private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!WideLowCategories.contains(fieldType.category)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (fieldType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ assert sourceRegisterType != null;
+
+ //per CodeVerify.c in dalvik:
+ //java generates synthetic functions that write byte values into boolean fields
+ if (sourceRegisterType.category == RegisterType.Category.Byte &&
+ instructionCategory == RegisterType.Category.Boolean) {
+
+ sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
+ }
+
+ RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
+ if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
+ throw new ValidationException(String.format("Cannot use %s with source register type %s.",
+ analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
+ }
+
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifyIputWide(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!WideLowCategories.contains(fieldType.category)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifyIputObject(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitThisCategories);
+
+ RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
+ ReferenceCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ if (objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
+ throw new ValidationException(String.format("Cannot access field %s through type %s",
+ field.getFieldString(), objectRegisterType.type.getClassType()));
+ }
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (fieldType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+
+ if (sourceRegisterType.category != RegisterType.Category.Null &&
+ !fieldType.type.isInterface() &&
+ !sourceRegisterType.type.extendsClass(fieldType.type)) {
+
+ throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
+ sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
+ }
+ }
+
+ private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(instructionCategory, null));
+ }
+
+ private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) {
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
+ }
+
+ private void verifySgetWide(AnalyzedInstruction analyzedInstruction) {
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+
+ if (fieldType.category != RegisterType.Category.LongLo &&
+ fieldType.category != RegisterType.Category.DoubleLo) {
+
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifySgetObject(AnalyzedInstruction analyzedInstruction) {
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (fieldType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category instructionCategory) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ assert sourceRegisterType != null;
+
+ //per CodeVerify.c in dalvik:
+ //java generates synthetic functions that write byte values into boolean fields
+ if (sourceRegisterType.category == RegisterType.Category.Byte &&
+ instructionCategory == RegisterType.Category.Boolean) {
+
+ sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
+ }
+
+ RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
+ if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
+ throw new ValidationException(String.format("Cannot use %s with source register type %s.",
+ analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
+ }
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifySputWide(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (!WideLowCategories.contains(fieldType.category)) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+ }
+
+ private void verifySputObject(AnalyzedInstruction analyzedInstruction) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
+ ReferenceCategories);
+
+ //TODO: check access
+ Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
+ assert referencedItem instanceof FieldIdItem;
+ FieldIdItem field = (FieldIdItem)referencedItem;
+
+ RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
+
+ if (fieldType.category != RegisterType.Category.Reference) {
+ throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
+ "for the instruction.", analyzedInstruction.instruction.opcode.name,
+ field.getFieldString()));
+ }
+
+ if (sourceRegisterType.category != RegisterType.Category.Null &&
+ !fieldType.type.isInterface() &&
+ !sourceRegisterType.type.extendsClass(fieldType.type)) {
+
+ throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
+ sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
+ }
+ }
+
+ private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) {
+ FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
+ analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction));
+ }
+
+ private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) {
+ FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
+ verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction));
+ }
+
+ private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) {
+ RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
+ analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction));
+ }
+
+ private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) {
+ RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
+ verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction));
+ }
+
+ private static final int INVOKE_VIRTUAL = 0x01;
+ private static final int INVOKE_SUPER = 0x02;
+ private static final int INVOKE_DIRECT = 0x04;
+ private static final int INVOKE_INTERFACE = 0x08;
+ private static final int INVOKE_STATIC = 0x10;
+
+ private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) {
+ //the only time that an invoke instruction changes a register type is when using invoke-direct on a
+ //constructor (<init>) method, which changes the uninitialized reference (and any register that the same
+ //uninit reference has been copied to) to an initialized reference
+
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
+ MethodIdItem methodIdItem = (MethodIdItem)item;
+
+ if (!methodIdItem.getMethodName().getStringValue().equals("<init>")) {
+ return;
+ }
+
+ RegisterType objectRegisterType;
+ //the object register is always the first register
+ int objectRegister = registers.getRegister();
+
+ objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
+ assert objectRegisterType != null;
+
+ if (objectRegisterType.category != RegisterType.Category.UninitRef &&
+ objectRegisterType.category != RegisterType.Category.UninitThis) {
+ return;
+ }
+
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister,
+ RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type));
+
+ for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
+ RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i];
+ if (postInstructionRegisterType.category == RegisterType.Category.Unknown) {
+ RegisterType preInstructionRegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(i);
+
+ if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
+ preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
+
+ RegisterType registerType;
+ if (preInstructionRegisterType == objectRegisterType) {
+ registerType = analyzedInstruction.postRegisterMap[objectRegister];
+ } else {
+ registerType = preInstructionRegisterType;
+ }
+
+ setPostRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType);
+ }
+ }
+ }
+ }
+
+ private void verifyInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType,
+ RegisterIterator registers) {
+ InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
+
+ //TODO: check access
+
+ Item item = instruction.getReferencedItem();
+ assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
+ MethodIdItem methodIdItem = (MethodIdItem)item;
+
+ TypeIdItem methodClass = methodIdItem.getContainingClass();
+ boolean isInit = false;
+
+ if (methodIdItem.getMethodName().getStringValue().charAt(0) == '<') {
+ if ((invokeType & INVOKE_DIRECT) != 0) {
+ isInit = true;
+ } else {
+ throw new ValidationException(String.format("Cannot call constructor %s with %s",
+ methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name));
+ }
+ }
+
+ ClassPath.ClassDef methodClassDef = ClassPath.getClassDef(methodClass);
+ if ((invokeType & INVOKE_INTERFACE) != 0) {
+ if (!methodClassDef.isInterface()) {
+ throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an interface " +
+ "class.", methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
+ methodClassDef.getClassType()));
+ }
+ } else {
+ if (methodClassDef.isInterface()) {
+ throw new ValidationException(String.format("Cannot call method %s with %s. %s is an interface class." +
+ " Use invoke-interface or invoke-interface/range instead.", methodIdItem.getMethodString(),
+ analyzedInstruction.instruction.opcode.name, methodClassDef.getClassType()));
+ }
+ }
+
+ if ((invokeType & INVOKE_SUPER) != 0) {
+ ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass());
+ if (currentMethodClassDef.getSuperclass() == null) {
+ throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
+ methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
+ methodClassDef.getSuperclass().getClassType()));
+ }
+
+ if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) {
+ throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " +
+ "of the current class %s", methodIdItem.getMethodString(),
+ analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(),
+ encodedMethod.method.getContainingClass().getTypeDescriptor()));
+ }
+
+ if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getShortMethodString())) {
+ throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
+ "no such method", methodIdItem.getMethodString(),
+ analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
+ }
+ }
+
+ assert isRange || registers.getCount() <= 5;
+
+ TypeListItem typeListItem = methodIdItem.getPrototype().getParameters();
+ int methodParameterRegisterCount;
+ if (typeListItem == null) {
+ methodParameterRegisterCount = 0;
+ } else {
+ methodParameterRegisterCount = typeListItem.getRegisterCount();
+ }
+
+ if ((invokeType & INVOKE_STATIC) == 0) {
+ methodParameterRegisterCount++;
+ }
+
+ if (methodParameterRegisterCount != registers.getCount()) {
+ throw new ValidationException(String.format("The number of registers does not match the number of " +
+ "parameters for method %s. Expecting %d registers, got %d.", methodIdItem.getMethodString(),
+ methodParameterRegisterCount + 1, registers.getCount()));
+ }
+
+ RegisterType objectRegisterType = null;
+ int objectRegister = 0;
+ if ((invokeType & INVOKE_STATIC) == 0) {
+ objectRegister = registers.getRegister();
+ registers.moveNext();
+
+ objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
+ assert objectRegisterType != null;
+ if (objectRegisterType.category == RegisterType.Category.UninitRef ||
+ objectRegisterType.category == RegisterType.Category.UninitThis) {
+
+ if (!isInit) {
+ throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
+ "reference type %s", methodIdItem.getMethodString(),
+ objectRegisterType.type.getClassType()));
+ }
+ } else if (objectRegisterType.category == RegisterType.Category.Reference) {
+ if (isInit) {
+ throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s",
+ methodIdItem.getMethodString(), objectRegisterType.type.getClassType()));
+ }
+ } else if (objectRegisterType.category == RegisterType.Category.Null) {
+ if (isInit) {
+ throw new ValidationException(String.format("Cannot invoke %s on a null reference",
+ methodIdItem.getMethodString()));
+ }
+ }
+ else {
+ throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s",
+ methodIdItem.getMethodString(), objectRegisterType.toString()));
+ }
+
+ if (isInit) {
+ if (objectRegisterType.type.getSuperclass() == methodClassDef) {
+ if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
+ throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
+ "match the method type exactly", methodIdItem.getMethodString(),
+ objectRegisterType.type.getClassType()));
+ }
+ }
+ }
+
+ if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null &&
+ !objectRegisterType.type.extendsClass(methodClassDef)) {
+
+ throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
+ "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
+ methodClassDef.getClassType()));
+ }
+ }
+
+ if (typeListItem != null) {
+ List<TypeIdItem> parameterTypes = typeListItem.getTypes();
+ int parameterTypeIndex = 0;
+ while (!registers.pastEnd()) {
+ assert parameterTypeIndex < parameterTypes.size();
+ RegisterType parameterType =
+ RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
+
+ int register = registers.getRegister();
+
+ RegisterType parameterRegisterType;
+ if (WideLowCategories.contains(parameterType.category)) {
+ parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
+
+ if (!registers.moveNext()) {
+ throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
+ parameterTypeIndex+1));
+ }
+ int nextRegister = registers.getRegister();
+
+ if (nextRegister != register + 1) {
+ throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
+ "must be consecutive.", register, nextRegister));
+ }
+ } else {
+ parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
+ }
+
+ assert parameterRegisterType != null;
+
+ if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
+ throw new ValidationException(
+ String.format("Invalid register type %s for parameter %d %s.",
+ parameterRegisterType.toString(), parameterTypeIndex+1,
+ parameterType.toString()));
+ }
+
+ parameterTypeIndex++;
+ registers.moveNext();
+ }
+ }
+ }
+
+ private void analyzeUnaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory) {
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(destRegisterCategory, null));
+ }
+
+ private void verifyUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories);
+ }
+
+ private void analyzeBinaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory,
+ boolean checkForBoolean) {
+ if (checkForBoolean) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType source1RegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+ RegisterType source2RegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
+
+ if (BooleanCategories.contains(source1RegisterType.category) &&
+ BooleanCategories.contains(source2RegisterType.category)) {
+
+ destRegisterCategory = RegisterType.Category.Boolean;
+ }
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(destRegisterCategory, null));
+ }
+
+ private void verifyBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
+ EnumSet validSource2Categories) {
+ ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource1Categories);
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validSource2Categories);
+ }
+
+ private void analyzeBinary2AddrOp(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category destRegisterCategory, boolean checkForBoolean) {
+ if (checkForBoolean) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType source1RegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
+ RegisterType source2RegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+
+ if (BooleanCategories.contains(source1RegisterType.category) &&
+ BooleanCategories.contains(source2RegisterType.category)) {
+
+ destRegisterCategory = RegisterType.Category.Boolean;
+ }
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(destRegisterCategory, null));
+ }
+
+ private void verifyBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
+ EnumSet validSource2Categories) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), validSource1Categories);
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource2Categories);
+ }
+
+ private void analyzeLiteralBinaryOp(AnalyzedInstruction analyzedInstruction,
+ RegisterType.Category destRegisterCategory, boolean checkForBoolean) {
+ if (checkForBoolean) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType sourceRegisterType =
+ analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
+
+ if (BooleanCategories.contains(sourceRegisterType.category)) {
+ long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
+ if (literal == 0 || literal == 1) {
+ destRegisterCategory = RegisterType.Category.Boolean;
+ }
+ }
+ }
+
+ setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
+ RegisterType.getRegisterType(destRegisterCategory, null));
+ }
+
+ private void verifyLiteralBinaryOp(AnalyzedInstruction analyzedInstruction) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
+ }
+
+ private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction,
+ boolean signedShift) {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ Primitive32BitCategories);
+ long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
+
+ if (literalShift == 0) {
+ return sourceRegisterType.category;
+ }
+
+ RegisterType.Category destRegisterCategory;
+ if (!signedShift) {
+ destRegisterCategory = RegisterType.Category.Integer;
+ } else {
+ destRegisterCategory = sourceRegisterType.category;
+ }
+
+ if (literalShift >= 32) {
+ //TODO: add warning
+ return destRegisterCategory;
+ }
+
+ switch (sourceRegisterType.category) {
+ case Integer:
+ case Float:
+ if (!signedShift) {
+ if (literalShift > 24) {
+ return RegisterType.Category.PosByte;
+ }
+ if (literalShift >= 16) {
+ return RegisterType.Category.Char;
+ }
+ } else {
+ if (literalShift >= 24) {
+ return RegisterType.Category.Byte;
+ }
+ if (literalShift >= 16) {
+ return RegisterType.Category.Short;
+ }
+ }
+ break;
+ case Short:
+ if (signedShift && literalShift >= 8) {
+ return RegisterType.Category.Byte;
+ }
+ break;
+ case PosShort:
+ if (literalShift >= 8) {
+ return RegisterType.Category.PosByte;
+ }
+ break;
+ case Char:
+ if (literalShift > 8) {
+ return RegisterType.Category.PosByte;
+ }
+ break;
+ case Byte:
+ break;
+ case PosByte:
+ return RegisterType.Category.PosByte;
+ case Null:
+ case One:
+ case Boolean:
+ return RegisterType.Category.Null;
+ default:
+ assert false;
+ }
+
+ return destRegisterCategory;
+ }
+
+
+ private void analyzeExecuteInline(AnalyzedInstruction analyzedInstruction) {
+ if (deodexUtil == null) {
+ throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
+ }
+
+ Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction;
+
+ DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction);
+ MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil);
+ if (inlineMethodIdItem == null) {
+ throw new ValidationException(String.format("Cannot load inline method with index %d",
+ instruction.getInlineIndex()));
+ }
+
+ Opcode deodexedOpcode = null;
+ switch (inlineMethod.methodType) {
+ case DeodexUtil.Direct:
+ deodexedOpcode = Opcode.INVOKE_DIRECT;
+ break;
+ case DeodexUtil.Static:
+ deodexedOpcode = Opcode.INVOKE_STATIC;
+ break;
+ case DeodexUtil.Virtual:
+ deodexedOpcode = Opcode.INVOKE_VIRTUAL;
+ break;
+ default:
+ assert false;
+ }
+
+ Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(),
+ instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
+ instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem);
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ analyzeInstruction(analyzedInstruction);
+ }
+
+ private void analyzeExecuteInlineRange(AnalyzedInstruction analyzedInstruction) {
+ if (deodexUtil == null) {
+ throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
+ }
+
+ Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction;
+
+ DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction);
+ MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil);
+ if (inlineMethodIdItem == null) {
+ throw new ValidationException(String.format("Cannot load inline method with index %d",
+ instruction.getInlineIndex()));
+ }
+
+ Opcode deodexedOpcode = null;
+ switch (inlineMethod.methodType) {
+ case DeodexUtil.Direct:
+ deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE;
+ break;
+ case DeodexUtil.Static:
+ deodexedOpcode = Opcode.INVOKE_STATIC_RANGE;
+ break;
+ case DeodexUtil.Virtual:
+ deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE;
+ break;
+ default:
+ assert false;
+ }
+
+ Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, (short)instruction.getRegCount(),
+ instruction.getStartRegister(), inlineMethodIdItem);
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ analyzeInstruction(analyzedInstruction);
+ }
+
+ private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) {
+ analyzeInvokeDirectEmpty(analyzedInstruction, true);
+ }
+
+ private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
+ Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
+
+ Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(),
+ instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
+ instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem());
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ if (analyzeResult) {
+ analyzeInstruction(analyzedInstruction);
+ }
+ }
+
+ private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction) {
+ analyzeInvokeObjectInitRange(analyzedInstruction, true);
+ }
+
+ private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
+ Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+
+ Instruction3rc deodexedInstruction = new Instruction3rc(Opcode.INVOKE_DIRECT_RANGE,
+ (short)instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem());
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ if (analyzeResult) {
+ analyzeInstruction(analyzedInstruction);
+ }
+ }
+
+ private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) {
+ Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction;
+
+ int fieldOffset = instruction.getFieldOffset();
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
+ ReferenceOrUninitCategories);
+
+ if (objectRegisterType.category == RegisterType.Category.Null) {
+ return false;
+ }
+
+ ClassPath.ClassDef accessingClass =
+ ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
+ if (accessingClass == null) {
+ throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
+ this.encodedMethod.method.getContainingClass()));
+ }
+
+ FieldIdItem fieldIdItem = deodexUtil.lookupField(accessingClass, objectRegisterType.type, fieldOffset);
+ if (fieldIdItem == null) {
+ throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d",
+ objectRegisterType.type.getClassType(), fieldOffset));
+ }
+
+ String fieldType = fieldIdItem.getFieldType().getTypeDescriptor();
+
+ Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, instruction.opcode);
+
+ Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
+ (byte)instruction.getRegisterB(), fieldIdItem);
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ analyzeInstruction(analyzedInstruction);
+
+ return true;
+ }
+
+ private boolean analyzeInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper,
+ boolean isRange) {
+ int methodIndex;
+ int objectRegister;
+
+
+ if (isRange) {
+ Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
+ methodIndex = instruction.getVtableIndex();
+ objectRegister = instruction.getStartRegister();
+ } else {
+ Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
+ methodIndex = instruction.getVtableIndex();
+ objectRegister = instruction.getRegisterD();
+ }
+
+ RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister,
+ ReferenceOrUninitCategories);
+
+ if (objectRegisterType.category == RegisterType.Category.Null) {
+ return false;
+ }
+
+ MethodIdItem methodIdItem = null;
+ ClassPath.ClassDef accessingClass =
+ ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
+ if (accessingClass == null) {
+ throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
+ this.encodedMethod.method.getContainingClass()));
+ }
+ if (isSuper) {
+ if (accessingClass.getSuperclass() != null) {
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass.getSuperclass(),
+ methodIndex);
+ }
+
+ if (methodIdItem == null) {
+ //it's possible that the pre-odexed instruction had used the method from the current class instead
+ //of from the superclass (although the superclass method is still what would actually be called).
+ //And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
+ //MethodIdItem for the method in the current class instead
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass, methodIndex);
+ }
+ } else{
+ methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, objectRegisterType.type, methodIndex);
+ }
+
+ if (methodIdItem == null) {
+ throw new ValidationException(String.format("Could not resolve the method in class %s at index %d",
+ objectRegisterType.type.getClassType(), methodIndex));
+ }
+
+
+ Instruction deodexedInstruction;
+ if (isRange) {
+ Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
+ Opcode opcode;
+ if (isSuper) {
+ opcode = Opcode.INVOKE_SUPER_RANGE;
+ } else {
+ opcode = Opcode.INVOKE_VIRTUAL_RANGE;
+ }
+
+ deodexedInstruction = new Instruction3rc(opcode, (short)instruction.getRegCount(),
+ instruction.getStartRegister(), methodIdItem);
+ } else {
+ Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
+ Opcode opcode;
+ if (isSuper) {
+ opcode = Opcode.INVOKE_SUPER;
+ } else {
+ opcode = Opcode.INVOKE_VIRTUAL;
+ }
+
+ deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(),
+ instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
+ instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem);
+ }
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+ analyzeInstruction(analyzedInstruction);
+
+ return true;
+ }
+
+ private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction) {
+ return analyzePutGetVolatile(analyzedInstruction, true);
+ }
+
+ private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
+ FieldIdItem fieldIdItem =
+ (FieldIdItem)(((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem());
+
+ String fieldType = fieldIdItem.getFieldType().getTypeDescriptor();
+
+ Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
+ analyzedInstruction.instruction.opcode);
+
+ Instruction deodexedInstruction;
+
+ if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) {
+ SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
+ if (analyzedInstruction.instruction.opcode.format == Format.Format21c) {
+ deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), fieldIdItem);
+ } else {
+ assert(analyzedInstruction.instruction.opcode.format == Format.Format41c);
+ deodexedInstruction = new Instruction41c(opcode, (byte)instruction.getRegisterA(), fieldIdItem);
+ }
+ } else {
+ TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
+
+ if (analyzedInstruction.instruction.opcode.format == Format.Format22c) {
+ deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
+ (byte)instruction.getRegisterB(), fieldIdItem);
+ } else {
+ assert(analyzedInstruction.instruction.opcode.format == Format.Format52c);
+ deodexedInstruction = new Instruction52c(opcode, (byte)instruction.getRegisterA(),
+ (byte)instruction.getRegisterB(), fieldIdItem);
+ }
+ }
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ if (analyzeResult) {
+ analyzeInstruction(analyzedInstruction);
+ }
+ return true;
+ }
+
+ private void analyzeInvokeObjectInitJumbo(AnalyzedInstruction analyzedInstruction) {
+ Instruction5rc instruction = (Instruction5rc)analyzedInstruction.instruction;
+
+ Instruction5rc deodexedInstruction = new Instruction5rc(Opcode.INVOKE_DIRECT_JUMBO,
+ instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem());
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+ analyzeInstruction(analyzedInstruction);
+ }
+
+ private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
+ RegisterType.Category instructionCategory) {
+ if (arrayFieldCategory == instructionCategory) {
+ return true;
+ }
+
+ if ((arrayFieldCategory == RegisterType.Category.Integer &&
+ instructionCategory == RegisterType.Category.Float) ||
+ (arrayFieldCategory == RegisterType.Category.Float &&
+ instructionCategory == RegisterType.Category.Integer)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber,
+ EnumSet validCategories) {
+ assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length;
+
+ RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber);
+ assert registerType != null;
+
+ checkRegister(registerType, registerNumber, validCategories);
+
+ if (validCategories == WideLowCategories) {
+ checkRegister(registerType, registerNumber, WideLowCategories);
+ checkWidePair(registerNumber, analyzedInstruction);
+
+ RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1);
+ assert secondRegisterType != null;
+ checkRegister(secondRegisterType, registerNumber+1, WideHighCategories);
+ }
+
+ return registerType;
+ }
+
+ private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) {
+ if (!validCategories.contains(registerType.category)) {
+ throw new ValidationException(String.format("Invalid register type %s for register v%d.",
+ registerType.toString(), registerNumber));
+ }
+ }
+
+ private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) {
+ if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) {
+ throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" +
+ "pair because it is the last register.", registerNumber));
+ }
+ }
+
+ private static interface RegisterIterator {
+ int getRegister();
+ boolean moveNext();
+ int getCount();
+ boolean pastEnd();
+ }
+
+ private static class Format35cRegisterIterator implements RegisterIterator {
+ private final int registerCount;
+ private final int[] registers;
+ private int currentRegister = 0;
+
+ public Format35cRegisterIterator(FiveRegisterInstruction instruction) {
+ registerCount = instruction.getRegCount();
+ registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(),
+ instruction.getRegisterF(), instruction.getRegisterG(),
+ instruction.getRegisterA()};
+ }
+
+ public int getRegister() {
+ return registers[currentRegister];
+ }
+
+ public boolean moveNext() {
+ currentRegister++;
+ return !pastEnd();
+ }
+
+ public int getCount() {
+ return registerCount;
+ }
+
+ public boolean pastEnd() {
+ return currentRegister >= registerCount;
+ }
+ }
+
+ private static class Format3rcRegisterIterator implements RegisterIterator {
+ private final int startRegister;
+ private final int registerCount;
+ private int currentRegister = 0;
+
+ public Format3rcRegisterIterator(RegisterRangeInstruction instruction) {
+ startRegister = instruction.getStartRegister();
+ registerCount = instruction.getRegCount();
+ }
+
+ public int getRegister() {
+ return startRegister + currentRegister;
+ }
+
+ public boolean moveNext() {
+ currentRegister++;
+ return !pastEnd();
+ }
+
+ public int getCount() {
+ return registerCount;
+ }
+
+ public boolean pastEnd() {
+ return currentRegister >= registerCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java
new file mode 100644
index 0000000..644cbd3
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java
@@ -0,0 +1,342 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.Code.Opcode;
+
+public class OdexedFieldInstructionMapper {
+ private static Opcode[][][][] opcodeMap = new Opcode[][][][] {
+ //get opcodes
+ new Opcode[][][] {
+ //iget quick
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_QUICK,
+ /*B*/ Opcode.IGET_QUICK,
+ /*S*/ Opcode.IGET_QUICK,
+ /*C*/ Opcode.IGET_QUICK,
+ /*I,F*/ Opcode.IGET_QUICK,
+ /*J,D*/ Opcode.IGET_WIDE_QUICK,
+ /*L,[*/ Opcode.IGET_OBJECT_QUICK
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_BOOLEAN,
+ /*B*/ Opcode.IGET_BYTE,
+ /*S*/ Opcode.IGET_SHORT,
+ /*C*/ Opcode.IGET_CHAR,
+ /*I,F*/ Opcode.IGET,
+ /*J,D*/ Opcode.IGET_WIDE,
+ /*L,[*/ Opcode.IGET_OBJECT
+ }
+ },
+ //iget volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_VOLATILE,
+ /*B*/ Opcode.IGET_VOLATILE,
+ /*S*/ Opcode.IGET_VOLATILE,
+ /*C*/ Opcode.IGET_VOLATILE,
+ /*I,F*/ Opcode.IGET_VOLATILE,
+ /*J,D*/ Opcode.IGET_WIDE_VOLATILE,
+ /*L,[*/ Opcode.IGET_OBJECT_VOLATILE
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_BOOLEAN,
+ /*B*/ Opcode.IGET_BYTE,
+ /*S*/ Opcode.IGET_SHORT,
+ /*C*/ Opcode.IGET_CHAR,
+ /*I,F*/ Opcode.IGET,
+ /*J,D*/ Opcode.IGET_WIDE,
+ /*L,[*/ Opcode.IGET_OBJECT
+ }
+ },
+ //sget volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.SGET_VOLATILE,
+ /*B*/ Opcode.SGET_VOLATILE,
+ /*S*/ Opcode.SGET_VOLATILE,
+ /*C*/ Opcode.SGET_VOLATILE,
+ /*I,F*/ Opcode.SGET_VOLATILE,
+ /*J,D*/ Opcode.SGET_WIDE_VOLATILE,
+ /*L,[*/ Opcode.SGET_OBJECT_VOLATILE
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.SGET_BOOLEAN,
+ /*B*/ Opcode.SGET_BYTE,
+ /*S*/ Opcode.SGET_SHORT,
+ /*C*/ Opcode.SGET_CHAR,
+ /*I,F*/ Opcode.SGET,
+ /*J,D*/ Opcode.SGET_WIDE,
+ /*L,[*/ Opcode.SGET_OBJECT
+ }
+ }
+ },
+ //put opcodes
+ new Opcode[][][] {
+ //iput quick
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_QUICK,
+ /*B*/ Opcode.IPUT_QUICK,
+ /*S*/ Opcode.IPUT_QUICK,
+ /*C*/ Opcode.IPUT_QUICK,
+ /*I,F*/ Opcode.IPUT_QUICK,
+ /*J,D*/ Opcode.IPUT_WIDE_QUICK,
+ /*L,[*/ Opcode.IPUT_OBJECT_QUICK
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_BOOLEAN,
+ /*B*/ Opcode.IPUT_BYTE,
+ /*S*/ Opcode.IPUT_SHORT,
+ /*C*/ Opcode.IPUT_CHAR,
+ /*I,F*/ Opcode.IPUT,
+ /*J,D*/ Opcode.IPUT_WIDE,
+ /*L,[*/ Opcode.IPUT_OBJECT
+ }
+ },
+ //iput volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_VOLATILE,
+ /*B*/ Opcode.IPUT_VOLATILE,
+ /*S*/ Opcode.IPUT_VOLATILE,
+ /*C*/ Opcode.IPUT_VOLATILE,
+ /*I,F*/ Opcode.IPUT_VOLATILE,
+ /*J,D*/ Opcode.IPUT_WIDE_VOLATILE,
+ /*L,[*/ Opcode.IPUT_OBJECT_VOLATILE
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_BOOLEAN,
+ /*B*/ Opcode.IPUT_BYTE,
+ /*S*/ Opcode.IPUT_SHORT,
+ /*C*/ Opcode.IPUT_CHAR,
+ /*I,F*/ Opcode.IPUT,
+ /*J,D*/ Opcode.IPUT_WIDE,
+ /*L,[*/ Opcode.IPUT_OBJECT
+ }
+ },
+ //sput volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.SPUT_VOLATILE,
+ /*B*/ Opcode.SPUT_VOLATILE,
+ /*S*/ Opcode.SPUT_VOLATILE,
+ /*C*/ Opcode.SPUT_VOLATILE,
+ /*I,F*/ Opcode.SPUT_VOLATILE,
+ /*J,D*/ Opcode.SPUT_WIDE_VOLATILE,
+ /*L,[*/ Opcode.SPUT_OBJECT_VOLATILE
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.SPUT_BOOLEAN,
+ /*B*/ Opcode.SPUT_BYTE,
+ /*S*/ Opcode.SPUT_SHORT,
+ /*C*/ Opcode.SPUT_CHAR,
+ /*I,F*/ Opcode.SPUT,
+ /*J,D*/ Opcode.SPUT_WIDE,
+ /*L,[*/ Opcode.SPUT_OBJECT
+ }
+ }
+ }
+ };
+
+ private static Opcode[][][][] jumboOpcodeMap = new Opcode[][][][] {
+ //get opcodes
+ new Opcode[][][] {
+ //iget volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_VOLATILE_JUMBO,
+ /*B*/ Opcode.IGET_VOLATILE_JUMBO,
+ /*S*/ Opcode.IGET_VOLATILE_JUMBO,
+ /*C*/ Opcode.IGET_VOLATILE_JUMBO,
+ /*I,F*/ Opcode.IGET_VOLATILE_JUMBO,
+ /*J,D*/ Opcode.IGET_WIDE_VOLATILE_JUMBO,
+ /*L,[*/ Opcode.IGET_OBJECT_VOLATILE_JUMBO
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IGET_BOOLEAN_JUMBO,
+ /*B*/ Opcode.IGET_BYTE_JUMBO,
+ /*S*/ Opcode.IGET_SHORT_JUMBO,
+ /*C*/ Opcode.IGET_CHAR_JUMBO,
+ /*I,F*/ Opcode.IGET_JUMBO,
+ /*J,D*/ Opcode.IGET_WIDE_JUMBO,
+ /*L,[*/ Opcode.IGET_OBJECT_JUMBO
+ }
+ },
+ //sget volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.SGET_VOLATILE_JUMBO,
+ /*B*/ Opcode.SGET_VOLATILE_JUMBO,
+ /*S*/ Opcode.SGET_VOLATILE_JUMBO,
+ /*C*/ Opcode.SGET_VOLATILE_JUMBO,
+ /*I,F*/ Opcode.SGET_VOLATILE_JUMBO,
+ /*J,D*/ Opcode.SGET_WIDE_VOLATILE_JUMBO,
+ /*L,[*/ Opcode.SGET_OBJECT_VOLATILE_JUMBO
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.SGET_BOOLEAN_JUMBO,
+ /*B*/ Opcode.SGET_BYTE_JUMBO,
+ /*S*/ Opcode.SGET_SHORT_JUMBO,
+ /*C*/ Opcode.SGET_CHAR_JUMBO,
+ /*I,F*/ Opcode.SGET_JUMBO,
+ /*J,D*/ Opcode.SGET_WIDE_JUMBO,
+ /*L,[*/ Opcode.SGET_OBJECT_JUMBO
+ }
+ }
+ },
+ //put opcodes
+ new Opcode[][][] {
+ //iput volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_VOLATILE_JUMBO,
+ /*B*/ Opcode.IPUT_VOLATILE_JUMBO,
+ /*S*/ Opcode.IPUT_VOLATILE_JUMBO,
+ /*C*/ Opcode.IPUT_VOLATILE_JUMBO,
+ /*I,F*/ Opcode.IPUT_VOLATILE_JUMBO,
+ /*J,D*/ Opcode.IPUT_WIDE_VOLATILE_JUMBO,
+ /*L,[*/ Opcode.IPUT_OBJECT_VOLATILE_JUMBO
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.IPUT_BOOLEAN_JUMBO,
+ /*B*/ Opcode.IPUT_BYTE_JUMBO,
+ /*S*/ Opcode.IPUT_SHORT_JUMBO,
+ /*C*/ Opcode.IPUT_CHAR_JUMBO,
+ /*I,F*/ Opcode.IPUT_JUMBO,
+ /*J,D*/ Opcode.IPUT_WIDE_JUMBO,
+ /*L,[*/ Opcode.IPUT_OBJECT_JUMBO
+ }
+ },
+ //sput volatile
+ new Opcode[][] {
+ //odexed
+ new Opcode[] {
+ /*Z*/ Opcode.SPUT_VOLATILE_JUMBO,
+ /*B*/ Opcode.SPUT_VOLATILE_JUMBO,
+ /*S*/ Opcode.SPUT_VOLATILE_JUMBO,
+ /*C*/ Opcode.SPUT_VOLATILE_JUMBO,
+ /*I,F*/ Opcode.SPUT_VOLATILE_JUMBO,
+ /*J,D*/ Opcode.SPUT_WIDE_VOLATILE_JUMBO,
+ /*L,[*/ Opcode.SPUT_OBJECT_VOLATILE_JUMBO
+ },
+ //deodexed
+ new Opcode[] {
+ /*Z*/ Opcode.SPUT_BOOLEAN_JUMBO,
+ /*B*/ Opcode.SPUT_BYTE_JUMBO,
+ /*S*/ Opcode.SPUT_SHORT_JUMBO,
+ /*C*/ Opcode.SPUT_CHAR_JUMBO,
+ /*I,F*/ Opcode.SPUT_JUMBO,
+ /*J,D*/ Opcode.SPUT_WIDE_JUMBO,
+ /*L,[*/ Opcode.SPUT_OBJECT_JUMBO
+ }
+ }
+ }
+ };
+
+ private static int getTypeIndex(char type) {
+ switch (type) {
+ case 'Z':
+ return 0;
+ case 'B':
+ return 1;
+ case 'S':
+ return 2;
+ case 'C':
+ return 3;
+ case 'I':
+ case 'F':
+ return 4;
+ case 'J':
+ case 'D':
+ return 5;
+ case 'L':
+ case '[':
+ return 6;
+ default:
+ }
+ throw new RuntimeException(String.format("Unknown type %s: ", type));
+ }
+
+ private static int getOpcodeSubtype(Opcode opcode) {
+ if (opcode.isOdexedInstanceQuick()) {
+ return 0;
+ } else if (opcode.isOdexedInstanceVolatile()) {
+ return 1;
+ } else if (opcode.isOdexedStaticVolatile()) {
+ return 2;
+ }
+ throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
+ }
+
+ static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(String fieldType, Opcode odexedOpcode) {
+ boolean jumbo = odexedOpcode.isJumboOpcode();
+ int opcodeType = odexedOpcode.setsRegister()?0:1;
+ int opcodeSubType = getOpcodeSubtype(odexedOpcode);
+ int typeIndex = getTypeIndex(fieldType.charAt(0));
+
+ Opcode correctOdexedOpcode, deodexedOpcode;
+
+ if (jumbo) {
+ correctOdexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][0][typeIndex];
+ deodexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][1][typeIndex];
+ } else {
+ correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
+ deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
+ }
+
+ if (correctOdexedOpcode != odexedOpcode) {
+ throw new ValidationException(String.format("Incorrect field type \"%s\" for %s", fieldType,
+ odexedOpcode.name));
+ }
+
+ return deodexedOpcode;
+ }
+}
+
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java
new file mode 100644
index 0000000..ed67732
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java
@@ -0,0 +1,322 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.TypeIdItem;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+
+import static org.jf.dexlib.Code.Analysis.ClassPath.ClassDef;
+
+public class RegisterType {
+ private final static HashMap<RegisterType, RegisterType> internedRegisterTypes =
+ new HashMap<RegisterType, RegisterType>();
+
+ public final Category category;
+ public final ClassDef type;
+
+ private RegisterType(Category category, ClassDef type) {
+ assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) &&
+ type != null) ||
+ ((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) &&
+ type == null);
+
+ this.category = category;
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + category.name() + (type==null?"":("," + type.getClassType())) + ")";
+ }
+
+ public void writeTo(Writer writer) throws IOException {
+ writer.write('(');
+ writer.write(category.name());
+ if (type != null) {
+ writer.write(',');
+ writer.write(type.getClassType());
+ }
+ writer.write(')');
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RegisterType that = (RegisterType) o;
+
+ if (category != that.category) return false;
+ if (type != null ? !type.equals(that.type) : that.type != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = category.hashCode();
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ return result;
+ }
+
+ public static enum Category {
+ //the Unknown category denotes a register type that hasn't been determined yet
+ Unknown,
+ Uninit,
+ Null,
+ One,
+ Boolean,
+ Byte,
+ PosByte,
+ Short,
+ PosShort,
+ Char,
+ Integer,
+ Float,
+ LongLo,
+ LongHi,
+ DoubleLo,
+ DoubleHi,
+ //the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
+ UninitRef,
+ //the UninitThis category is used the "this" register inside an <init> method, before the superclass' <init>
+ //method is called
+ UninitThis,
+ Reference,
+ //This is used when there are multiple incoming execution paths that have incompatible register types. For
+ //example if the register's type is an Integer on one incomming code path, but is a Reference type on another
+ //incomming code path. There is no register type that can hold either an Integer or a Reference.
+ Conflicted;
+
+ //this table is used when merging register types. For example, if a particular register can be either a Byte
+ //or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can
+ //could hold either type of value.
+ protected static Category[][] mergeTable =
+ {
+ /* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/
+ /*Unknown*/ {Unknown, Uninit, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, Reference, Conflicted},
+ /*Uninit*/ {Uninit, Uninit, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Null*/ {Null, Conflicted, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
+ /*One*/ {One, Conflicted, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Boolean*/ {Boolean, Conflicted, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Byte*/ {Byte, Conflicted, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*PosByte*/ {PosByte, Conflicted, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Short*/ {Short, Conflicted, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*PosShort*/ {PosShort, Conflicted, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Char*/ {Char, Conflicted, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Integer*/ {Integer, Conflicted, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*Float*/ {Float, Conflicted, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted},
+ /*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted},
+ /*Reference*/ {Reference, Conflicted, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
+ /*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
+ };
+
+ //this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
+ //example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an
+ //array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()]
+ //Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to
+ //check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
+ protected static boolean[][] assigmentTable =
+ {
+ /* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/
+ /*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ /*Uninit*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ /*Null*/ {false, false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false},
+ /*One*/ {false, false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
+ /*Boolean*/ {false, false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
+ /*Byte*/ {false, false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false},
+ /*PosByte*/ {false, false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false},
+ /*Short*/ {false, false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false},
+ /*PosShort*/ {false, false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false},
+ /*Char*/ {false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false},
+ /*Integer*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
+ /*Float*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false},
+ /*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
+ /*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
+ /*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false},
+ /*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
+ /*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ /*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ /*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
+ /*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
+ /*----------*/
+ /*value type*/
+ };
+
+ }
+
+ public static RegisterType getRegisterTypeForType(String type) {
+ switch (type.charAt(0)) {
+ case 'V':
+ throw new ValidationException("The V type can only be used as a method return type");
+ case 'Z':
+ return getRegisterType(Category.Boolean, null);
+ case 'B':
+ return getRegisterType(Category.Byte, null);
+ case 'S':
+ return getRegisterType(Category.Short, null);
+ case 'C':
+ return getRegisterType(Category.Char, null);
+ case 'I':
+ return getRegisterType(Category.Integer, null);
+ case 'F':
+ return getRegisterType(Category.Float, null);
+ case 'J':
+ return getRegisterType(Category.LongLo, null);
+ case 'D':
+ return getRegisterType(Category.DoubleLo, null);
+ case 'L':
+ case '[':
+ return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
+ default:
+ throw new RuntimeException("Invalid type: " + type);
+ }
+ }
+
+ public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
+ return getRegisterTypeForType(typeIdItem.getTypeDescriptor());
+ }
+
+ public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
+ if (typeIdItem.getRegisterCount() == 1) {
+ throw new RuntimeException("Cannot use this method for non-wide register type: " +
+ typeIdItem.getTypeDescriptor());
+ }
+
+ switch (typeIdItem.getTypeDescriptor().charAt(0)) {
+ case 'J':
+ if (firstRegister) {
+ return getRegisterType(Category.LongLo, null);
+ } else {
+ return getRegisterType(Category.LongHi, null);
+ }
+ case 'D':
+ if (firstRegister) {
+ return getRegisterType(Category.DoubleLo, null);
+ } else {
+ return getRegisterType(Category.DoubleHi, null);
+ }
+ default:
+ throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor());
+ }
+ }
+
+ public static RegisterType getRegisterTypeForLiteral(long literalValue) {
+ if (literalValue < -32768) {
+ return getRegisterType(Category.Integer, null);
+ }
+ if (literalValue < -128) {
+ return getRegisterType(Category.Short, null);
+ }
+ if (literalValue < 0) {
+ return getRegisterType(Category.Byte, null);
+ }
+ if (literalValue == 0) {
+ return getRegisterType(Category.Null, null);
+ }
+ if (literalValue == 1) {
+ return getRegisterType(Category.One, null);
+ }
+ if (literalValue < 128) {
+ return getRegisterType(Category.PosByte, null);
+ }
+ if (literalValue < 32768) {
+ return getRegisterType(Category.PosShort, null);
+ }
+ if (literalValue < 65536) {
+ return getRegisterType(Category.Char, null);
+ }
+ return getRegisterType(Category.Integer, null);
+ }
+
+ public RegisterType merge(RegisterType type) {
+ if (type == null || type == this) {
+ return this;
+ }
+
+ Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
+
+ ClassDef mergedType = null;
+ if (mergedCategory == Category.Reference) {
+ if (this.type instanceof ClassPath.UnresolvedClassDef ||
+ type.type instanceof ClassPath.UnresolvedClassDef) {
+ mergedType = ClassPath.getUnresolvedObjectClassDef();
+ } else {
+ mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
+ }
+ } else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
+ if (this.category == Category.Unknown) {
+ return type;
+ }
+ assert type.category == Category.Unknown;
+ return this;
+ }
+ return RegisterType.getRegisterType(mergedCategory, mergedType);
+ }
+
+ public boolean canBeAssignedTo(RegisterType slotType) {
+ if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) {
+ if (this.category == Category.Reference && slotType.category == Category.Reference) {
+ if (!slotType.type.isInterface()) {
+ return this.type.extendsClass(slotType.type);
+ }
+ //for verification, we assume all objects implement all interfaces, so we don't verify the type if
+ //slotType is an interface
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static RegisterType getUnitializedReference(ClassDef classType) {
+ //We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance
+ //is used to track a specific uninitialized reference, so that if multiple registers contain the same
+ //uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate
+ //<init> is invoked
+ return new RegisterType(Category.UninitRef, classType);
+ }
+
+ public static RegisterType getRegisterType(Category category, ClassDef classType) {
+ RegisterType newRegisterType = new RegisterType(category, classType);
+ RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
+ if (internedRegisterType == null) {
+ internedRegisterTypes.put(newRegisterType, newRegisterType);
+ return newRegisterType;
+ }
+ return internedRegisterType;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java
new file mode 100644
index 0000000..5ad98fe
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java
@@ -0,0 +1,140 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.*;
+import org.jf.dexlib.Code.Format.Instruction22c;
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Util.AccessFlags;
+
+import java.util.HashMap;
+
+public class SyntheticAccessorResolver {
+ public static final int METHOD = 0;
+ public static final int GETTER = 1;
+ public static final int SETTER = 2;
+
+ private final DexFileClassMap classMap;
+ private final HashMap<MethodIdItem, AccessedMember> resolvedAccessors = new HashMap<MethodIdItem, AccessedMember>();
+
+ public SyntheticAccessorResolver(DexFile dexFile) {
+ classMap = new DexFileClassMap(dexFile);
+ }
+
+ public static boolean looksLikeSyntheticAccessor(MethodIdItem methodIdItem) {
+ return methodIdItem.getMethodName().getStringValue().startsWith("access$");
+ }
+
+ public AccessedMember getAccessedMember(MethodIdItem methodIdItem) {
+ AccessedMember accessedMember = resolvedAccessors.get(methodIdItem);
+ if (accessedMember != null) {
+ return accessedMember;
+ }
+
+ ClassDefItem classDefItem = classMap.getClassDefByType(methodIdItem.getContainingClass());
+ if (classDefItem == null) {
+ return null;
+ }
+
+ ClassDataItem classDataItem = classDefItem.getClassData();
+ if (classDataItem == null) {
+ return null;
+ }
+
+ ClassDataItem.EncodedMethod encodedMethod = classDataItem.findDirectMethodByMethodId(methodIdItem);
+ if (encodedMethod == null) {
+ return null;
+ }
+
+ //A synthetic accessor will be marked synthetic
+ if ((encodedMethod.accessFlags & AccessFlags.SYNTHETIC.getValue()) == 0) {
+ return null;
+ }
+
+ Instruction[] instructions = encodedMethod.codeItem.getInstructions();
+
+ //TODO: add support for odexed formats
+ switch (instructions[0].opcode.format) {
+ case Format35c:
+ case Format3rc: {
+ //a synthetic method access should be either 2 or 3 instructions, depending on if the method returns
+ //anything or not
+ if (instructions.length < 2 || instructions.length > 3) {
+ return null;
+ }
+ InstructionWithReference instruction = (InstructionWithReference)instructions[0];
+ Item referencedItem = instruction.getReferencedItem();
+ if (!(referencedItem instanceof MethodIdItem)) {
+ return null;
+ }
+ MethodIdItem referencedMethodIdItem = (MethodIdItem)referencedItem;
+
+ accessedMember = new AccessedMember(METHOD, referencedMethodIdItem);
+ resolvedAccessors.put(methodIdItem, accessedMember);
+ return accessedMember;
+ }
+ case Format22c: {
+ //a synthetic field access should be exactly 2 instructions. The set/put, and then the return
+ if (instructions.length != 2) {
+ return null;
+ }
+ Instruction22c instruction = (Instruction22c)instructions[0];
+ Item referencedItem = instruction.getReferencedItem();
+ if (!(referencedItem instanceof FieldIdItem)) {
+ return null;
+ }
+ FieldIdItem referencedFieldIdItem = (FieldIdItem)referencedItem;
+
+ if (instruction.opcode.setsRegister() || instruction.opcode.setsWideRegister()) {
+ //If the instruction sets a register, that means it is a getter - it gets the field value and
+ //stores it in the register
+ accessedMember = new AccessedMember(GETTER, referencedFieldIdItem);
+ } else {
+ accessedMember = new AccessedMember(SETTER, referencedFieldIdItem);
+ }
+
+ resolvedAccessors.put(methodIdItem, accessedMember);
+ return accessedMember;
+ }
+ default:
+ return null;
+ }
+ }
+
+ public static class AccessedMember {
+ public final int accessedMemberType;
+ public final Item accessedMember;
+
+ public AccessedMember(int accessedMemberType, Item accessedMember) {
+ this.accessedMemberType = accessedMemberType;
+ this.accessedMember = accessedMember;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java
new file mode 100644
index 0000000..32c13cb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java
@@ -0,0 +1,52 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Analysis;
+
+import org.jf.dexlib.Util.ExceptionWithContext;
+
+public class ValidationException extends ExceptionWithContext {
+ private int codeAddress;
+
+ public ValidationException(int codeAddress, String errorMessage) {
+ super(errorMessage);
+ this.codeAddress = codeAddress;
+ }
+
+ public ValidationException(String errorMessage) {
+ super(errorMessage);
+ }
+
+ public void setCodeAddress(int codeAddress) {
+ this.codeAddress = codeAddress;
+ }
+
+ public int getCodeAddress() {
+ return codeAddress;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java
new file mode 100644
index 0000000..a772dfe
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface EncodedLiteralInstruction extends LiteralInstruction {
+ long getDecodedLiteral();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java
new file mode 100644
index 0000000..b9ff066
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java
@@ -0,0 +1,37 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface FiveRegisterInstruction extends InvokeInstruction {
+ byte getRegisterA();
+ byte getRegisterD();
+ byte getRegisterE();
+ byte getRegisterF();
+ byte getRegisterG();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java
new file mode 100644
index 0000000..5de88f4
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java
@@ -0,0 +1,152 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import java.util.Iterator;
+
+public class ArrayDataPseudoInstruction extends Instruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private int elementWidth;
+ private byte[] encodedValues;
+
+ @Override
+ public int getSize(int codeAddress) {
+ return ((encodedValues.length + 1)/2) + 4 + (codeAddress % 2);
+ }
+
+ public ArrayDataPseudoInstruction(int elementWidth, byte[] encodedValues) {
+ super(Opcode.NOP);
+
+ if (encodedValues.length % elementWidth != 0) {
+ throw new RuntimeException("There are not a whole number of " + elementWidth + " byte elements");
+ }
+
+ this.elementWidth = elementWidth;
+ this.encodedValues = encodedValues;
+ }
+
+ public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) {
+ super(Opcode.NOP);
+
+ byte opcodeByte = buffer[bufferIndex];
+ if (opcodeByte != 0x00) {
+ throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction");
+ }
+
+ byte subopcodeByte = buffer[bufferIndex+1];
+ if (subopcodeByte != 0x03) {
+ throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction");
+ }
+
+ this.elementWidth = NumberUtils.decodeUnsignedShort(buffer, bufferIndex+2);
+ int elementCount = NumberUtils.decodeInt(buffer, bufferIndex+4);
+ this.encodedValues = new byte[elementCount * elementWidth];
+ System.arraycopy(buffer, bufferIndex+8, encodedValues, 0, elementCount * elementWidth);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.alignTo(4);
+
+ int elementCount = encodedValues.length / elementWidth;
+
+ out.writeByte(0x00);
+ out.writeByte(0x03);
+ out.writeShort(elementWidth);
+ out.writeInt(elementCount);
+ out.write(encodedValues);
+
+ //make sure we're written out an even number of bytes
+ out.alignTo(2);
+ }
+
+ protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
+ "fill-array-data instruction");
+ }
+
+ public Format getFormat() {
+ return Format.ArrayData;
+ }
+
+ public int getElementWidth() {
+ return elementWidth;
+ }
+
+ public int getElementCount() {
+ return encodedValues.length / elementWidth;
+ }
+
+ public static class ArrayElement {
+ public final byte[] buffer;
+ public int bufferIndex;
+ public final int elementWidth;
+ public ArrayElement(byte[] buffer, int elementWidth) {
+ this.buffer = buffer;
+ this.elementWidth = elementWidth;
+ }
+ }
+
+ public Iterator<ArrayElement> getElements() {
+ return new Iterator<ArrayElement>() {
+ final int elementCount = getElementCount();
+ int i=0;
+ int position=0;
+ final ArrayElement arrayElement = new ArrayElement(encodedValues, getElementWidth());
+
+ public boolean hasNext() {
+ return i<elementCount;
+ }
+
+ public ArrayElement next() {
+ arrayElement.bufferIndex = position;
+ position += arrayElement.elementWidth;
+ i++;
+ return arrayElement;
+ }
+
+ public void remove() {
+ }
+ };
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ if (opcode != Opcode.NOP) {
+ throw new RuntimeException("The opcode for an ArrayDataPseudoInstruction must be NOP");
+ }
+ return new ArrayDataPseudoInstruction(buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java
new file mode 100644
index 0000000..a606405
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Format.java
@@ -0,0 +1,86 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+
+public enum Format {
+ Format10t(Instruction10t.Factory, 2),
+ Format10x(Instruction10x.Factory, 2),
+ Format11n(Instruction11n.Factory, 2),
+ Format11x(Instruction11x.Factory, 2),
+ Format12x(Instruction12x.Factory, 2),
+ Format20bc(Instruction20bc.Factory, 4),
+ Format20t(Instruction20t.Factory, 4),
+ Format21c(Instruction21c.Factory, 4),
+ Format21h(Instruction21h.Factory, 4),
+ Format21s(Instruction21s.Factory, 4),
+ Format21t(Instruction21t.Factory, 4),
+ Format22b(Instruction22b.Factory, 4),
+ Format22c(Instruction22c.Factory, 4),
+ Format22cs(Instruction22cs.Factory, 4),
+ Format22s(Instruction22s.Factory, 4),
+ Format22t(Instruction22t.Factory, 4),
+ Format22x(Instruction22x.Factory, 4),
+ Format23x(Instruction23x.Factory, 4),
+ Format30t(Instruction30t.Factory, 6),
+ Format31c(Instruction31c.Factory, 6),
+ Format31i(Instruction31i.Factory, 6),
+ Format31t(Instruction31t.Factory, 6),
+ Format32x(Instruction32x.Factory, 6),
+ Format35c(Instruction35c.Factory, 6),
+ Format35mi(Instruction35mi.Factory, 6),
+ Format35ms(Instruction35ms.Factory, 6),
+ Format3rc(Instruction3rc.Factory, 6),
+ Format3rmi(Instruction3rmi.Factory, 6),
+ Format3rms(Instruction3rms.Factory, 6),
+ Format41c(Instruction41c.Factory, 8),
+ Format51l(Instruction51l.Factory, 10),
+ Format52c(Instruction52c.Factory, 10),
+ Format5rc(Instruction5rc.Factory, 10),
+ ArrayData(null, -1, true),
+ PackedSwitchData(null, -1, true),
+ SparseSwitchData(null, -1, true),
+ UnresolvedOdexInstruction(null, -1, false),
+ ;
+
+ public final Instruction.InstructionFactory Factory;
+ public final int size;
+ public final boolean variableSizeFormat;
+
+ private Format(Instruction.InstructionFactory factory, int size) {
+ this(factory, size, false);
+ }
+
+ private Format(Instruction.InstructionFactory factory, int size, boolean variableSizeFormat) {
+ this.Factory = factory;
+ this.size = size;
+ this.variableSizeFormat = variableSizeFormat;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10t.java
new file mode 100644
index 0000000..4b7f872
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10t.java
@@ -0,0 +1,92 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class Instruction10t extends OffsetInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private int targetAddressOffset;
+
+ public Instruction10t(Opcode opcode, int offA) {
+ super(opcode);
+ this.targetAddressOffset = offA;
+
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead.");
+ }
+
+ //allow out of range address offsets here, so we have the option of replacing this instruction
+ //with goto/16 or goto/32 later
+ }
+
+ private Instruction10t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert buffer[bufferIndex] == opcode.value;
+
+ this.targetAddressOffset = buffer[bufferIndex + 1];
+ assert targetAddressOffset != 0;
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead");
+ }
+
+ if (targetAddressOffset < -128 || targetAddressOffset > 127) {
+ throw new RuntimeException("The address offset is out of range. It must be in [-128,-1] or [1, 127]");
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte(targetAddressOffset);
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ this.targetAddressOffset = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format10t;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction10t(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java
new file mode 100644
index 0000000..46ea968
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java
@@ -0,0 +1,64 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class Instruction10x extends Instruction {
+ public static final InstructionFactory Factory = new Factory();
+
+ public Instruction10x(Opcode opcode) {
+ super(opcode);
+ }
+
+ public Instruction10x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert (buffer[bufferIndex] & 0xFF) == opcode.value;
+ assert buffer[bufferIndex + 1] == 0x00;
+ }
+
+ public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(0);
+ }
+
+ public Format getFormat() {
+ return Format.Format10x;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction10x(opcode, buffer, bufferIndex);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java
new file mode 100644
index 0000000..7fae04c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java
@@ -0,0 +1,89 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction11n extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte litB;
+
+ public Instruction11n(Opcode opcode, byte regA, byte litB) {
+ super(opcode);
+
+ if (regA >= 1 << 4) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ if (litB < -(1 << 3) ||
+ litB >= 1 << 3) {
+ throw new RuntimeException("The literal value must be between -8 and 7 inclusive");
+ }
+
+ this.regA = regA;
+ this.litB = litB;
+ }
+
+ private Instruction11n(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.litB = NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]);
+ }
+
+ public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((litB << 4) | regA);
+ }
+
+ public Format getFormat() {
+ return Format.Format11n;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public long getLiteral() {
+ return litB;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction11n(opcode, buffer, bufferIndex);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java
new file mode 100644
index 0000000..ca30c7b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java
@@ -0,0 +1,76 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction11x extends Instruction implements SingleRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+
+ public Instruction11x(Opcode opcode, short regA) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ }
+
+ private Instruction11x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ }
+
+ public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ }
+
+ public Format getFormat() {
+ return Format.Format11x;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction11x(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java
new file mode 100644
index 0000000..6d8d774
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java
@@ -0,0 +1,83 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction12x extends Instruction implements TwoRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private int regA;
+ private int regB;
+
+ public Instruction12x(Opcode opcode, byte regA, byte regB) {
+ super(opcode);
+
+ if (regA >= 1 << 4 ||
+ regB >= 1 << 4) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ this.regA = regA;
+ this.regB = regB;
+ }
+
+ private Instruction12x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ }
+
+ public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regB << 4) | regA);
+ }
+
+ public Format getFormat() {
+ return Format.Format12x;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public int getRegisterB() {
+ return regB;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction12x(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java
new file mode 100644
index 0000000..75af9df
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java
@@ -0,0 +1,100 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.*;
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction20bc extends InstructionWithReference {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+
+ private VerificationErrorType validationErrorType;
+
+ public Instruction20bc(Opcode opcode, VerificationErrorType validationErrorType, Item referencedItem) {
+ super(opcode, referencedItem, getReferenceType(referencedItem));
+
+ this.validationErrorType = validationErrorType;
+ }
+
+ private static ReferenceType getReferenceType(Item item) {
+ if (item instanceof TypeIdItem) {
+ return ReferenceType.type;
+ }
+ if (item instanceof FieldIdItem) {
+ return ReferenceType.field;
+ }
+ if (item instanceof MethodIdItem) {
+ return ReferenceType.method;
+ }
+ return null;
+ }
+
+ private Instruction20bc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
+ validationErrorType = VerificationErrorType.getValidationErrorType(val & 0x3f);
+ }
+
+ protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) {
+ short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]);
+ short referenceType = (short)(val >> 6);
+ return ReferenceType.fromValidationErrorReferenceType(referenceType);
+ }
+
+ @Override
+ public Format getFormat() {
+ return Format.Format20bc;
+ }
+
+ @Override
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if(opcode == Opcode.CONST_STRING && getReferencedItem().getIndex() > 0xFFFF) {
+ throw new RuntimeException("String offset is too large for const-string. Use string-const/jumbo instead.");
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte((this.getReferenceType().getValidationErrorReferenceType() << 6) &
+ validationErrorType.getValue());
+
+ out.writeShort(getReferencedItem().getIndex());
+ }
+
+ public VerificationErrorType getValidationErrorType() {
+ return validationErrorType;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction20bc(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java
new file mode 100644
index 0000000..f970cfb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java
@@ -0,0 +1,94 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction20t extends OffsetInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private int targetAddressOffset;
+
+ public Instruction20t(Opcode opcode, int offA) {
+ super(opcode);
+ this.targetAddressOffset = offA;
+
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead.");
+ }
+
+ //allow out of range address offsets here, so we have the option of replacing this instruction
+ //with goto/16 or goto/32 later
+ }
+
+ private Instruction20t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert buffer[bufferIndex] == opcode.value;
+
+ this.targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex+2);
+ assert targetAddressOffset != 0;
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead");
+ }
+
+ if (targetAddressOffset < -32768 || targetAddressOffset > 32767) {
+ throw new RuntimeException("The address offset is out of range. It must be in [-32768,-1] or [1, 32768]");
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte(0x00);
+ out.writeShort(targetAddressOffset);
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ this.targetAddressOffset = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format20t;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction20t(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java
new file mode 100644
index 0000000..2c12a9b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java
@@ -0,0 +1,115 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class Instruction21c extends InstructionWithReference implements SingleRegisterInstruction,
+ InstructionWithJumboVariant {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+
+ public Instruction21c(Opcode opcode, short regA, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ if (opcode == Opcode.NEW_INSTANCE) {
+ assert referencedItem instanceof TypeIdItem;
+ if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') {
+ throw new RuntimeException("Only class references can be used with the new-instance opcode");
+ }
+ }
+
+ this.regA = (byte)regA;
+ }
+
+ private Instruction21c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ if (opcode == Opcode.NEW_INSTANCE &&
+ ((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') {
+
+ throw new RuntimeException("Only class references can be used with the new-instance opcode");
+ }
+
+ this.regA = buffer[bufferIndex + 1];
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if(getReferencedItem().getIndex() > 0xFFFF) {
+ if (opcode.hasJumboOpcode()) {
+ throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.",
+ opcode.referenceType.name(), opcode.getJumboOpcode().name));
+ } else {
+ throw new RuntimeException(String.format("%s index is too large", opcode.referenceType.name()));
+ }
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeShort(getReferencedItem().getIndex());
+ }
+
+ public Format getFormat() {
+ return Format.Format21c;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public Instruction makeJumbo() {
+ Opcode jumboOpcode = opcode.getJumboOpcode();
+ if (jumboOpcode == null) {
+ return null;
+ }
+
+ if (jumboOpcode.format == Format.Format31c) {
+ return new Instruction31c(jumboOpcode, (short)getRegisterA(), getReferencedItem());
+ }
+
+ return new Instruction41c(jumboOpcode, getRegisterA(), getReferencedItem());
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction21c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java
new file mode 100644
index 0000000..c063b9a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java
@@ -0,0 +1,94 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.EncodedLiteralInstruction;
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction21h extends Instruction implements SingleRegisterInstruction, EncodedLiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private short litB;
+
+ public Instruction21h(Opcode opcode, short regA, short litB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.litB = litB;
+ }
+
+ private Instruction21h(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeShort(litB);
+ }
+
+ public Format getFormat() {
+ return Format.Format21h;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public long getLiteral() {
+ return litB;
+ }
+
+ public long getDecodedLiteral() {
+ if (opcode == Opcode.CONST_HIGH16) {
+ return litB << 16;
+ } else {
+ assert opcode == Opcode.CONST_WIDE_HIGH16;
+ return ((long)litB) << 48;
+ }
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction21h(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java
new file mode 100644
index 0000000..ca50769
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction21s extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private short litB;
+
+ public Instruction21s(Opcode opcode, short regA, short litB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.litB = litB;
+ }
+
+ private Instruction21s(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeShort(litB);
+ }
+
+ public Format getFormat() {
+ return Format.Format21s;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public long getLiteral() {
+ return litB;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction21s(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java
new file mode 100644
index 0000000..f1cd7d2
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java
@@ -0,0 +1,103 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction21t extends OffsetInstruction implements SingleRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private short targetAddressOffset;
+
+ public Instruction21t(Opcode opcode, short regA, short offB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ if (offB == 0) {
+ throw new RuntimeException("The address offset cannot be 0.");
+ }
+
+ this.regA = (byte)regA;
+ this.targetAddressOffset = offB;
+ }
+
+ private Instruction21t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert buffer[bufferIndex] == opcode.value;
+
+ regA = buffer[bufferIndex + 1];
+ targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2);
+ assert targetAddressOffset != 0;
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeShort(targetAddressOffset);
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ if (targetAddressOffset < Short.MIN_VALUE || targetAddressOffset > Short.MAX_VALUE) {
+ throw new RuntimeException("The address offset " + targetAddressOffset +
+ " is out of range. It must be in [-32768, 32767]");
+ }
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0");
+ }
+ this.targetAddressOffset = (short) targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format21t;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction21t(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java
new file mode 100644
index 0000000..19fdbd5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java
@@ -0,0 +1,93 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class Instruction22b extends Instruction implements TwoRegisterInstruction, LiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+ private byte litC;
+
+ public Instruction22b(Opcode opcode, short regA, short regB, byte litC) {
+ super(opcode);
+
+ if (regA >= 1 << 8 ||
+ regB >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.regB = (byte)regB;
+ this.litC = litC;
+ }
+
+ private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.regB = buffer[bufferIndex + 2];
+ this.litC = buffer[bufferIndex + 3];
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeByte(regB);
+ out.writeByte(litC);
+ }
+
+ public Format getFormat() {
+ return Format.Format22b;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public int getRegisterB() {
+ return regB & 0xFF;
+ }
+
+ public long getLiteral() {
+ return litC;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22b(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java
new file mode 100644
index 0000000..5833a66
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java
@@ -0,0 +1,106 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction,
+ InstructionWithJumboVariant {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+
+ public Instruction22c(Opcode opcode, byte regA, byte regB, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regA >= 1 << 4 ||
+ regB >= 1 << 4) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ this.regA = regA;
+ this.regB = regB;
+ }
+
+ private Instruction22c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if(getReferencedItem().getIndex() > 0xFFFF) {
+ if (opcode.hasJumboOpcode()) {
+ throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.",
+ opcode.referenceType.name(), opcode.getJumboOpcode().name));
+ } else {
+ throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name()));
+ }
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte((regB << 4) | regA);
+ out.writeShort(getReferencedItem().getIndex());
+ }
+
+ public Format getFormat() {
+ return Format.Format22c;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public int getRegisterB() {
+ return regB;
+ }
+
+ public Instruction makeJumbo() {
+ Opcode jumboOpcode = opcode.getJumboOpcode();
+ if (jumboOpcode == null) {
+ return null;
+ }
+
+ return new Instruction52c(jumboOpcode, getRegisterA(), getRegisterB(), getReferencedItem());
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java
new file mode 100644
index 0000000..9dc62fa
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java
@@ -0,0 +1,97 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OdexedFieldAccess;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction22cs extends Instruction implements TwoRegisterInstruction, OdexedFieldAccess {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+ private short fieldOffset;
+
+ public Instruction22cs(Opcode opcode, byte regA, byte regB, int fieldOffset) {
+ super(opcode);
+
+ if (regA >= 1 << 4 ||
+ regB >= 1 << 4) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ if (fieldOffset >= 1 << 16) {
+ throw new RuntimeException("The field offset must be less than 65536");
+ }
+
+ this.regA = regA;
+ this.regB = regB;
+ this.fieldOffset = (short)fieldOffset;
+ }
+
+ private Instruction22cs(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ this.fieldOffset = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regB << 4) | regA);
+ out.writeShort(fieldOffset);
+ }
+
+ public Format getFormat() {
+ return Format.Format22cs;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public int getRegisterB() {
+ return regB;
+ }
+
+ public int getFieldOffset() {
+ return fieldOffset & 0xFFFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22cs(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java
new file mode 100644
index 0000000..434a1b2
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java
@@ -0,0 +1,93 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction22s extends Instruction implements TwoRegisterInstruction, LiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+ private short litC;
+
+ public Instruction22s(Opcode opcode, byte regA, byte regB, short litC) {
+ super(opcode);
+
+ if (regA >= 1 << 4 ||
+ regB >= 1 << 4) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ this.regA = regA;
+ this.regB = regB;
+ this.litC = litC;
+ }
+
+ private Instruction22s(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ this.litC = NumberUtils.decodeShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regB << 4) | regA);
+ out.writeShort(litC);
+ }
+
+ public Format getFormat() {
+ return Format.Format22s;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public int getRegisterB() {
+ return regB;
+ }
+
+ public long getLiteral() {
+ return litC;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22s(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java
new file mode 100644
index 0000000..56b8298
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java
@@ -0,0 +1,112 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction22t extends OffsetInstruction implements TwoRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+ private short targetAddressOffset;
+
+ public Instruction22t(Opcode opcode, byte regA, byte regB, short offC) {
+ super(opcode);
+
+ if (regA >= 16 ||
+ regB >= 16) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ if (offC == 0) {
+ throw new RuntimeException("The address offset cannot be 0.");
+ }
+
+ this.regA = regA;
+ this.regB = regB;
+ this.targetAddressOffset = offC;
+ }
+
+ private Instruction22t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert buffer[bufferIndex] == opcode.value;
+
+ regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2);
+
+ assert targetAddressOffset != 0;
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regB << 4) | regA);
+ out.writeShort(targetAddressOffset);
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ if (targetAddressOffset < -32768 || targetAddressOffset > 32767) {
+ throw new RuntimeException("The address offset " + targetAddressOffset +
+ " is out of range. It must be in [-32768, 32767]");
+ }
+ if (targetAddressOffset == 0) {
+ throw new RuntimeException("The address offset cannot be 0");
+ }
+ this.targetAddressOffset = (short)targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format22t;
+ }
+
+ public int getRegisterA() {
+ return regA;
+ }
+
+ public int getRegisterB() {
+ return regB;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22t(opcode, buffer, bufferIndex);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java
new file mode 100644
index 0000000..dc26289
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java
@@ -0,0 +1,88 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction22x extends Instruction implements TwoRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private short regB;
+
+ public Instruction22x(Opcode opcode, short regA, int regB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v16");
+ }
+
+ if (regB >= 1 << 16) {
+ throw new RuntimeException("The register number must be less than v65536");
+ }
+
+ this.regA = (byte)regA;
+ this.regB = (short)regB;
+ }
+
+ private Instruction22x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeShort(regB);
+ }
+
+ public Format getFormat() {
+ return Format.Format22x;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public int getRegisterB() {
+ return regB & 0xFFFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction22x(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java
new file mode 100644
index 0000000..b9695fc
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java
@@ -0,0 +1,93 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.ThreeRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class Instruction23x extends Instruction implements ThreeRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private byte regB;
+ private byte regC;
+
+ public Instruction23x(Opcode opcode, short regA, short regB, short regC) {
+ super(opcode);
+
+ if (regA >= 1 << 8 ||
+ regB >= 1 << 8 ||
+ regC >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.regB = (byte)regB;
+ this.regC = (byte)regC;
+ }
+
+ private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.regB = buffer[bufferIndex + 2];
+ this.regC = buffer[bufferIndex + 3];
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeByte(regB);
+ out.writeByte(regC);
+ }
+
+ public Format getFormat() {
+ return Format.Format23x;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public int getRegisterB() {
+ return regB & 0xFF;
+ }
+
+ public int getRegisterC() {
+ return regC & 0xFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction23x(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java
new file mode 100644
index 0000000..fc83b22
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java
@@ -0,0 +1,78 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction30t extends OffsetInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private int targetAddressOffset;
+
+ public Instruction30t(Opcode opcode, int offA) {
+ super(opcode);
+ this.targetAddressOffset = offA;
+ }
+
+ private Instruction30t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ assert buffer[bufferIndex] == opcode.value;
+
+ this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex+2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(0x00);
+ out.writeInt(targetAddressOffset);
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ this.targetAddressOffset = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format30t;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction30t(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java
new file mode 100644
index 0000000..4c62c00
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java
@@ -0,0 +1,79 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction31c extends InstructionWithJumboReference implements SingleRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+
+ public Instruction31c(Opcode opcode, short regA, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ }
+
+ private Instruction31c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regA = buffer[bufferIndex + 1];
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeInt(getReferencedItem().getIndex());
+ }
+
+ public Format getFormat() {
+ return Format.Format31c;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction31c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java
new file mode 100644
index 0000000..5c08ce4
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction31i extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private int litB;
+
+ public Instruction31i(Opcode opcode, short regA, int litB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.litB = litB;
+ }
+
+ private Instruction31i(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ this.litB = NumberUtils.decodeInt(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeInt(litB);
+ }
+
+ public Format getFormat() {
+ return Format.Format31i;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public long getLiteral() {
+ return litB;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction31i(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java
new file mode 100644
index 0000000..55bcc6b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java
@@ -0,0 +1,90 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction31t extends OffsetInstruction implements SingleRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private int targetAddressOffset;
+
+ public Instruction31t(Opcode opcode, short regA, int offB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.targetAddressOffset = offB;
+ }
+
+ private Instruction31t(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = buffer[bufferIndex + 1];
+ this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ //align the address offset so that the absolute address is aligned on a 4-byte boundary (2 code block boundary)
+ out.writeInt(targetAddressOffset + ((currentCodeAddress + targetAddressOffset) % 2));
+ }
+
+ public void updateTargetAddressOffset(int targetAddressOffset) {
+ this.targetAddressOffset = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.Format31t;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public int getTargetAddressOffset() {
+ return targetAddressOffset;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction31t(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java
new file mode 100644
index 0000000..841ab66
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java
@@ -0,0 +1,86 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction32x extends Instruction implements TwoRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private short regA;
+ private short regB;
+
+ public Instruction32x(Opcode opcode, int regA, int regB) {
+ super(opcode);
+
+ if (regA >= 1<<16 ||
+ regB >= 1<<16) {
+ throw new RuntimeException("The register number must be less than v65536");
+ }
+
+ this.regA = (short)regA;
+ this.regB = (short)regB;
+ }
+
+ private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(0);
+ out.writeShort(regA);
+ out.writeShort(regB);
+ }
+
+ public Format getFormat() {
+ return Format.Format32x;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFFFF;
+ }
+
+ public int getRegisterB() {
+ return regB & 0xFFFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction32x(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java
new file mode 100644
index 0000000..dfe69fe
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java
@@ -0,0 +1,170 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.FiveRegisterInstruction;
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import static org.jf.dexlib.Code.Opcode.*;
+
+public class Instruction35c extends InstructionWithReference implements FiveRegisterInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private byte regA;
+ private byte regD;
+ private byte regE;
+ private byte regF;
+ private byte regG;
+
+ public Instruction35c(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
+ byte regA, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regCount > 5) {
+ throw new RuntimeException("regCount cannot be greater than 5");
+ }
+
+ if (regD >= 1 << 4 ||
+ regE >= 1 << 4 ||
+ regF >= 1 << 4 ||
+ regG >= 1 << 4 ||
+ regA >= 1 << 4) {
+ throw new RuntimeException("All register args must fit in 4 bits");
+ }
+
+ checkItem(opcode, referencedItem, regCount);
+
+ this.regCount = (byte)regCount;
+ this.regA = regA;
+ this.regD = regD;
+ this.regE = regE;
+ this.regF = regF;
+ this.regG = regG;
+ }
+
+ protected Instruction35c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
+ this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
+
+ if (getRegCount() > 5) {
+ throw new RuntimeException("regCount cannot be greater than 5");
+ }
+
+ checkItem(opcode, getReferencedItem(), getRegCount());
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if(getReferencedItem().getIndex() > 0xFFFF) {
+ if (opcode.hasJumboOpcode()) {
+ throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.",
+ opcode.referenceType.name(), opcode.getJumboOpcode().name));
+ } else {
+ throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name()));
+ }
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte((regCount << 4) | regA);
+ out.writeShort(getReferencedItem().getIndex());
+ out.writeByte((regE << 4) | regD);
+ out.writeByte((regG << 4) | regF);
+ }
+
+ public Format getFormat() {
+ return Format.Format35c;
+ }
+
+ public int getRegCount() {
+ return regCount;
+ }
+
+ public byte getRegisterA() {
+ return regA;
+ }
+
+ public byte getRegisterD() {
+ return regD;
+ }
+
+ public byte getRegisterE() {
+ return regE;
+ }
+
+ public byte getRegisterF() {
+ return regF;
+ }
+
+ public byte getRegisterG() {
+ return regG;
+ }
+
+ private static void checkItem(Opcode opcode, Item item, int regCount) {
+ if (opcode == FILLED_NEW_ARRAY) {
+ //check data for filled-new-array opcode
+ String type = ((TypeIdItem) item).getTypeDescriptor();
+ if (type.charAt(0) != '[') {
+ throw new RuntimeException("The type must be an array type");
+ }
+ if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
+ throw new RuntimeException("The type cannot be an array of longs or doubles");
+ }
+ } else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value ||
+ opcode == INVOKE_DIRECT_EMPTY) {
+ //check data for invoke-* opcodes
+ MethodIdItem methodIdItem = (MethodIdItem) item;
+ int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
+ if (opcode != INVOKE_STATIC) {
+ parameterRegisterCount++;
+ }
+ if (parameterRegisterCount != regCount) {
+ throw new RuntimeException("regCount does not match the number of arguments of the method");
+ }
+ }
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction35c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java
new file mode 100644
index 0000000..a204cd7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+
+public class Instruction35mi extends Instruction implements FiveRegisterInstruction, OdexedInvokeInline {
+ public static final InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private byte regA;
+ private byte regD;
+ private byte regE;
+ private byte regF;
+ private byte regG;
+ private short inlineIndex;
+
+ public Instruction35mi(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
+ byte regA, int inlineIndex) {
+ super(opcode);
+ if (regCount > 5) {
+ throw new RuntimeException("regCount cannot be greater than 5");
+ }
+
+ if (regD >= 1 << 4 ||
+ regE >= 1 << 4 ||
+ regF >= 1 << 4 ||
+ regG >= 1 << 4 ||
+ regA >= 1 << 4) {
+ throw new RuntimeException("All register args must fit in 4 bits");
+ }
+
+ if (inlineIndex >= 1 << 16) {
+ throw new RuntimeException("The method index must be less than 65536");
+ }
+
+ this.regCount = (byte)regCount;
+ this.regA = regA;
+ this.regD = regD;
+ this.regE = regE;
+ this.regF = regF;
+ this.regG = regG;
+ this.inlineIndex = (short)inlineIndex;
+ }
+
+ private Instruction35mi(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
+ this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
+ this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regCount << 4) | regA);
+ out.writeShort(inlineIndex);
+ out.writeByte((regE << 4) | regD);
+ out.writeByte((regG << 4) | regF);
+ }
+
+ public Format getFormat() {
+ return Format.Format35mi;
+ }
+
+ public int getRegCount() {
+ return regCount;
+ }
+
+ public byte getRegisterA() {
+ return regA;
+ }
+
+ public byte getRegisterD() {
+ return regD;
+ }
+
+ public byte getRegisterE() {
+ return regE;
+ }
+
+ public byte getRegisterF() {
+ return regF;
+ }
+
+ public byte getRegisterG() {
+ return regG;
+ }
+
+ public int getInlineIndex() {
+ return inlineIndex & 0xFFFF;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction35mi(opcode, buffer, bufferIndex);
+ }
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java
new file mode 100644
index 0000000..8e9098c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java
@@ -0,0 +1,136 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.FiveRegisterInstruction;
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OdexedInvokeVirtual;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+
+public class Instruction35ms extends Instruction implements FiveRegisterInstruction, OdexedInvokeVirtual {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private byte regA;
+ private byte regD;
+ private byte regE;
+ private byte regF;
+ private byte regG;
+ private short vtableIndex;
+
+ public Instruction35ms(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
+ byte regA, int vtableIndex) {
+ super(opcode);
+ if (regCount > 5) {
+ throw new RuntimeException("regCount cannot be greater than 5");
+ }
+
+ if (regD >= 1 << 4 ||
+ regE >= 1 << 4 ||
+ regF >= 1 << 4 ||
+ regG >= 1 << 4 ||
+ regA >= 1 << 4) {
+ throw new RuntimeException("All register args must fit in 4 bits");
+ }
+
+ if (vtableIndex >= 1 << 16) {
+ throw new RuntimeException("The method index must be less than 65536");
+ }
+
+ this.regCount = (byte)regCount;
+ this.regA = regA;
+ this.regD = regD;
+ this.regE = regE;
+ this.regF = regF;
+ this.regG = regG;
+ this.vtableIndex = (short)vtableIndex;
+ }
+
+ private Instruction35ms(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
+ this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
+ this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
+ this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
+ this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte((regCount << 4) | regA);
+ out.writeShort(vtableIndex);
+ out.writeByte((regE << 4) | regD);
+ out.writeByte((regG << 4) | regF);
+ }
+
+ public Format getFormat() {
+ return Format.Format35ms;
+ }
+
+ public int getRegCount() {
+ return regCount;
+ }
+
+ public byte getRegisterA() {
+ return regA;
+ }
+
+ public byte getRegisterD() {
+ return regD;
+ }
+
+ public byte getRegisterE() {
+ return regE;
+ }
+
+ public byte getRegisterF() {
+ return regF;
+ }
+
+ public byte getRegisterG() {
+ return regG;
+ }
+
+ public int getVtableIndex() {
+ return vtableIndex & 0xFFFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction35ms(opcode, buffer, bufferIndex);
+ }
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java
new file mode 100644
index 0000000..c2abc62
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java
@@ -0,0 +1,148 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.RegisterRangeInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import static org.jf.dexlib.Code.Opcode.*;
+
+public class Instruction3rc extends InstructionWithReference implements RegisterRangeInstruction,
+ InstructionWithJumboVariant {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private short startReg;
+
+ public Instruction3rc(Opcode opcode, short regCount, int startReg, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regCount >= 1 << 8) {
+ throw new RuntimeException("regCount must be less than 256");
+ }
+ if (regCount < 0) {
+ throw new RuntimeException("regCount cannot be negative");
+ }
+
+ if (startReg >= 1 << 16) {
+ throw new RuntimeException("The beginning register of the range must be less than 65536");
+ }
+ if (startReg < 0) {
+ throw new RuntimeException("The beginning register of the range cannot be negative");
+ }
+
+ this.regCount = (byte)regCount;
+ this.startReg = (short)startReg;
+
+ checkItem(opcode, referencedItem, regCount);
+ }
+
+ private Instruction3rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
+
+ checkItem(opcode, getReferencedItem(), getRegCount());
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ if(getReferencedItem().getIndex() > 0xFFFF) {
+ if (opcode.hasJumboOpcode()) {
+ throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.",
+ opcode.referenceType.name(), opcode.getJumboOpcode().name));
+ } else {
+ throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name()));
+ }
+ }
+
+ out.writeByte(opcode.value);
+ out.writeByte(regCount);
+ out.writeShort(this.getReferencedItem().getIndex());
+ out.writeShort(startReg);
+ }
+
+ public Format getFormat() {
+ return Format.Format3rc;
+ }
+
+ public int getRegCount() {
+ return (short)(regCount & 0xFF);
+ }
+
+ public int getStartRegister() {
+ return startReg & 0xFFFF;
+ }
+
+ private static void checkItem(Opcode opcode, Item item, int regCount) {
+ if (opcode == FILLED_NEW_ARRAY_RANGE) {
+ //check data for filled-new-array/range opcode
+ String type = ((TypeIdItem) item).getTypeDescriptor();
+ if (type.charAt(0) != '[') {
+ throw new RuntimeException("The type must be an array type");
+ }
+ if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
+ throw new RuntimeException("The type cannot be an array of longs or doubles");
+ }
+ } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value ||
+ opcode == INVOKE_OBJECT_INIT_RANGE) {
+ //check data for invoke-*/range opcodes
+ MethodIdItem methodIdItem = (MethodIdItem) item;
+ int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
+ if (opcode != INVOKE_STATIC_RANGE) {
+ parameterRegisterCount++;
+ }
+ if (parameterRegisterCount != regCount) {
+ throw new RuntimeException("regCount does not match the number of arguments of the method");
+ }
+ }
+ }
+
+ public Instruction makeJumbo() {
+ Opcode jumboOpcode = opcode.getJumboOpcode();
+ if (jumboOpcode == null) {
+ return null;
+ }
+
+ return new Instruction5rc(jumboOpcode, getRegCount(), getStartRegister(), getReferencedItem());
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction3rc(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java
new file mode 100644
index 0000000..8499f6f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction3rmi extends Instruction implements RegisterRangeInstruction, OdexedInvokeInline {
+ public static final InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private short startReg;
+ private short inlineIndex;
+
+ public Instruction3rmi(Opcode opcode, short regCount, int startReg, int inlineIndex) {
+ super(opcode);
+
+ if (regCount >= 1 << 8) {
+ throw new RuntimeException("regCount must be less than 256");
+ }
+ if (regCount < 0) {
+ throw new RuntimeException("regCount cannot be negative");
+ }
+
+ if (startReg >= 1 << 16) {
+ throw new RuntimeException("The beginning register of the range must be less than 65536");
+ }
+ if (startReg < 0) {
+ throw new RuntimeException("The beginning register of the range cannot be negative");
+ }
+
+ if (inlineIndex >= 1 << 16) {
+ throw new RuntimeException("The method index must be less than 65536");
+ }
+
+ this.regCount = (byte)regCount;
+ this.startReg = (short)startReg;
+ this.inlineIndex = (short)inlineIndex;
+ }
+
+ private Instruction3rmi(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regCount);
+ out.writeShort(inlineIndex);
+ out.writeShort(startReg);
+ }
+
+ public Format getFormat() {
+ return Format.Format3rmi;
+ }
+
+ public int getRegCount() {
+ return (short)(regCount & 0xFF);
+ }
+
+ public int getStartRegister() {
+ return startReg & 0xFFFF;
+ }
+
+ public int getInlineIndex() {
+ return inlineIndex & 0xFFFF;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction3rmi(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java
new file mode 100644
index 0000000..a93c9f7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java
@@ -0,0 +1,107 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.OdexedInvokeVirtual;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.RegisterRangeInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction3rms extends Instruction implements RegisterRangeInstruction, OdexedInvokeVirtual {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regCount;
+ private short startReg;
+ private short vtableIndex;
+
+ public Instruction3rms(Opcode opcode, short regCount, int startReg, int vtableIndex) {
+ super(opcode);
+
+ if (regCount >= 1 << 8) {
+ throw new RuntimeException("regCount must be less than 256");
+ }
+ if (regCount < 0) {
+ throw new RuntimeException("regCount cannot be negative");
+ }
+
+ if (startReg >= 1 << 16) {
+ throw new RuntimeException("The beginning register of the range must be less than 65536");
+ }
+ if (startReg < 0) {
+ throw new RuntimeException("The beginning register of the range cannot be negative");
+ }
+
+ if (vtableIndex >= 1 << 16) {
+ throw new RuntimeException("The method index must be less than 65536");
+ }
+
+ this.regCount = (byte)regCount;
+ this.startReg = (short)startReg;
+ this.vtableIndex = (short)vtableIndex;
+ }
+
+ private Instruction3rms(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regCount);
+ out.writeShort(vtableIndex);
+ out.writeShort(startReg);
+ }
+
+ public Format getFormat() {
+ return Format.Format3rms;
+ }
+
+ public int getRegCount() {
+ return (short)(regCount & 0xFF);
+ }
+
+ public int getStartRegister() {
+ return startReg & 0xFFFF;
+ }
+
+ public int getVtableIndex() {
+ return vtableIndex & 0xFFFF;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction3rms(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java
new file mode 100644
index 0000000..ac83cbb
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction41c extends InstructionWithJumboReference implements SingleRegisterInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private short regA;
+
+ public Instruction41c(Opcode opcode, int regA, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regA >= 1 << 16) {
+ throw new RuntimeException("The register number must be less than v65536");
+ }
+
+ if (opcode == Opcode.NEW_INSTANCE_JUMBO) {
+ assert referencedItem instanceof TypeIdItem;
+ if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') {
+ throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode");
+ }
+ }
+
+ this.regA = (short)regA;
+ }
+
+ private Instruction41c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ if (opcode == Opcode.NEW_INSTANCE_JUMBO &&
+ ((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') {
+
+ throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode");
+ }
+
+ this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(0xFF);
+ out.writeByte(opcode.value);
+ out.writeInt(getReferencedItem().getIndex());
+ out.writeShort(getRegisterA());
+ }
+
+ public Format getFormat() {
+ return Format.Format41c;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFFFF;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction41c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java
new file mode 100644
index 0000000..a627c7a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java
@@ -0,0 +1,85 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.LiteralInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.SingleRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction51l extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private byte regA;
+ private long litB;
+
+ public Instruction51l(Opcode opcode, short regA, long litB) {
+ super(opcode);
+
+ if (regA >= 1 << 8) {
+ throw new RuntimeException("The register number must be less than v256");
+ }
+
+ this.regA = (byte)regA;
+ this.litB = litB;
+ }
+
+ private Instruction51l(Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
+ litB = NumberUtils.decodeLong(buffer, bufferIndex + 2);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(opcode.value);
+ out.writeByte(regA);
+ out.writeLong(litB);
+ }
+
+ public Format getFormat() {
+ return Format.Format51l;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFF;
+ }
+
+ public long getLiteral() {
+ return litB;
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction51l(opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java
new file mode 100644
index 0000000..0d7f3f5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.TwoRegisterInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+public class Instruction52c extends InstructionWithJumboReference implements TwoRegisterInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private short regA;
+ private short regB;
+
+ public Instruction52c(Opcode opcode, int regA, int regB, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regA >= 1 << 16) {
+ throw new RuntimeException("The register number must be less than v65536");
+ }
+
+ if (regB >= 1 << 16) {
+ throw new RuntimeException("The register number must be less than v65536");
+ }
+
+ this.regA = (short)regA;
+ this.regB = (short)regB;
+ }
+
+ private Instruction52c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
+ this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8);
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(0xFF);
+ out.writeByte(opcode.value);
+ out.writeInt(getReferencedItem().getIndex());
+ out.writeShort(getRegisterA());
+ out.writeShort(getRegisterB());
+ }
+
+ public Format getFormat() {
+ return Format.Format52c;
+ }
+
+ public int getRegisterA() {
+ return regA & 0xFFFF;
+ }
+
+ public int getRegisterB() {
+ return regB & 0xFFFF;
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction52c(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java
new file mode 100644
index 0000000..7c79e25
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.RegisterRangeInstruction;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import static org.jf.dexlib.Code.Opcode.*;
+
+public class Instruction5rc extends InstructionWithJumboReference implements RegisterRangeInstruction {
+ public static final InstructionFactory Factory = new Factory();
+ private short regCount;
+ private short startReg;
+
+ public Instruction5rc(Opcode opcode, int regCount, int startReg, Item referencedItem) {
+ super(opcode, referencedItem);
+
+ if (regCount >= 1 << 16) {
+ throw new RuntimeException("regCount must be less than 65536");
+ }
+ if (regCount < 0) {
+ throw new RuntimeException("regCount cannot be negative");
+ }
+
+ if (startReg >= 1 << 16) {
+ throw new RuntimeException("The beginning register of the range must be less than 65536");
+ }
+ if (startReg < 0) {
+ throw new RuntimeException("The beginning register of the range cannot be negative");
+ }
+
+ this.regCount = (short)regCount;
+ this.startReg = (short)startReg;
+
+ checkItem(opcode, referencedItem, regCount);
+ }
+
+ private Instruction5rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+
+ this.regCount = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6);
+ this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8);
+
+ checkItem(opcode, getReferencedItem(), getRegCount());
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.writeByte(0xff);
+ out.writeByte(opcode.value);
+ out.writeInt(this.getReferencedItem().getIndex());
+ out.writeShort(regCount);
+ out.writeShort(startReg);
+ }
+
+ public Format getFormat() {
+ return Format.Format5rc;
+ }
+
+ public int getRegCount() {
+ return regCount & 0xFFFF;
+ }
+
+ public int getStartRegister() {
+ return startReg & 0xFFFF;
+ }
+
+ private static void checkItem(Opcode opcode, Item item, int regCount) {
+ if (opcode == FILLED_NEW_ARRAY_JUMBO) {
+ //check data for filled-new-array/jumbo opcode
+ String type = ((TypeIdItem) item).getTypeDescriptor();
+ if (type.charAt(0) != '[') {
+ throw new RuntimeException("The type must be an array type");
+ }
+ if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
+ throw new RuntimeException("The type cannot be an array of longs or doubles");
+ }
+ } else if (opcode.value >= INVOKE_VIRTUAL_JUMBO.value && opcode.value <= INVOKE_INTERFACE_JUMBO.value ||
+ opcode == INVOKE_OBJECT_INIT_JUMBO) {
+ //check data for invoke-*/range opcodes
+ MethodIdItem methodIdItem = (MethodIdItem) item;
+ int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
+ if (opcode != INVOKE_STATIC_JUMBO) {
+ parameterRegisterCount++;
+ }
+ if (parameterRegisterCount != regCount) {
+ throw new RuntimeException("regCount does not match the number of arguments of the method");
+ }
+ }
+ }
+
+ private static class Factory implements InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ return new Instruction5rc(dexFile, opcode, buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java
new file mode 100644
index 0000000..c10e3bd
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.InstructionWithReference;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.Code.ReferenceType;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Item;
+import org.jf.dexlib.Util.NumberUtils;
+
+public abstract class InstructionWithJumboReference extends InstructionWithReference {
+ protected InstructionWithJumboReference(Opcode opcode, Item referencedItem) {
+ super(opcode, referencedItem);
+ }
+
+ protected InstructionWithJumboReference(Opcode opcode, Item referencedItem, ReferenceType referenceType) {
+ super(opcode, referencedItem, referenceType);
+ }
+
+ protected InstructionWithJumboReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(dexFile, opcode, buffer, bufferIndex);
+ }
+
+ protected int getReferencedItemIndex(byte[] buffer, int bufferIndex) {
+ return NumberUtils.decodeInt(buffer, bufferIndex + 2);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java
new file mode 100644
index 0000000..9557e69
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+
+public interface InstructionWithJumboVariant {
+ Instruction makeJumbo();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java
new file mode 100644
index 0000000..6e88861
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java
@@ -0,0 +1,158 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.MultiOffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import java.util.Iterator;
+
+public class PackedSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private int firstKey;
+ private int[] targets;
+
+ @Override
+ public int getSize(int codeAddress) {
+ return getTargetCount() * 2 + 4 + (codeAddress % 2);
+ }
+
+ public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) {
+ super(Opcode.NOP);
+
+ if (targets.length > 0xFFFF) {
+ throw new RuntimeException("The packed-switch data contains too many elements. " +
+ "The maximum number of switch elements is 65535");
+ }
+
+ this.firstKey = firstKey;
+ this.targets = targets;
+ }
+
+ public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
+ super(Opcode.NOP);
+
+ byte opcodeByte = buffer[bufferIndex];
+ if (opcodeByte != 0x00) {
+ throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction");
+ }
+ byte subopcodeByte = buffer[bufferIndex+1];
+ if (subopcodeByte != 0x01) {
+ throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction");
+ }
+
+ int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ this.firstKey = NumberUtils.decodeInt(buffer, bufferIndex + 4);
+ this.targets = new int[targetCount];
+
+ for (int i = 0; i<targetCount; i++) {
+ targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 8 + 4*i);
+ }
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.alignTo(4);
+
+ out.writeByte(0x00);
+ out.writeByte(0x01);
+ out.writeShort(targets.length);
+ out.writeInt(firstKey);
+
+ for (int target : targets) {
+ out.writeInt(target);
+ }
+ }
+
+ protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
+ "packed-switch-data instruction");
+ }
+
+ public void updateTarget(int targetIndex, int targetAddressOffset) {
+ targets[targetIndex] = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.PackedSwitchData;
+ }
+
+ public int getTargetCount() {
+ return targets.length;
+ }
+
+ public int getFirstKey() {
+ return firstKey;
+ }
+
+ public int[] getTargets() {
+ return targets;
+ }
+
+ public static class PackedSwitchTarget {
+ public int value;
+ public int targetAddressOffset;
+ }
+
+ public Iterator<PackedSwitchTarget> iterateKeysAndTargets() {
+ return new Iterator<PackedSwitchTarget>() {
+ final int targetCount = getTargetCount();
+ int i = 0;
+ int value = getFirstKey();
+
+ PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget();
+
+ public boolean hasNext() {
+ return i<targetCount;
+ }
+
+ public PackedSwitchTarget next() {
+ packedSwitchTarget.value = value++;
+ packedSwitchTarget.targetAddressOffset = targets[i];
+ i++;
+ return packedSwitchTarget;
+ }
+
+ public void remove() {
+ }
+ };
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ if (opcode != Opcode.NOP) {
+ throw new RuntimeException("The opcode for a PackedSwitchDataPseudoInstruction must be NOP");
+ }
+ return new PackedSwitchDataPseudoInstruction(buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java
new file mode 100644
index 0000000..06c295a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java
@@ -0,0 +1,177 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Code.MultiOffsetInstruction;
+import org.jf.dexlib.Code.Opcode;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.NumberUtils;
+
+import java.util.Iterator;
+
+public class SparseSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
+ public static final Instruction.InstructionFactory Factory = new Factory();
+ private int[] keys;
+ private int[] targets;
+
+ @Override
+ public int getSize(int codeAddress) {
+ return getTargetCount() * 4 + 2 + (codeAddress % 2);
+ }
+
+ public SparseSwitchDataPseudoInstruction(int[] keys, int[] targets) {
+ super(Opcode.NOP);
+
+ if (keys.length != targets.length) {
+ throw new RuntimeException("The number of keys and targets don't match");
+ }
+
+ if (targets.length == 0) {
+ throw new RuntimeException("The sparse-switch data must contain at least 1 key/target");
+ }
+
+ if (targets.length > 0xFFFF) {
+ throw new RuntimeException("The sparse-switch data contains too many elements. " +
+ "The maximum number of switch elements is 65535");
+ }
+
+ this.keys = keys;
+ this.targets = targets;
+ }
+
+ public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
+ super(Opcode.NOP);
+
+ byte opcodeByte = buffer[bufferIndex];
+ if (opcodeByte != 0x00) {
+ throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction");
+ }
+ byte subopcodeByte = buffer[bufferIndex+1];
+ if (subopcodeByte != 0x02) {
+ throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction");
+ }
+
+ int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ keys = new int[targetCount];
+ targets = new int[targetCount];
+
+ for (int i=0; i<targetCount; i++) {
+ keys[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + i*4);
+ targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + targetCount*4 + i*4);
+ }
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.alignTo(4);
+
+ out.writeByte(0x00);
+ out.writeByte(0x02);
+ out.writeShort(targets.length);
+
+ if (targets.length > 0) {
+ int key = keys[0];
+
+ out.writeInt(key);
+
+ for (int i = 1; i < keys.length; i++) {
+ key = keys[i];
+ assert key >= keys[i - 1];
+ out.writeInt(key);
+ }
+
+ for (int target : targets) {
+ out.writeInt(target);
+ }
+ }
+ }
+
+ protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
+ "sparse-switch-data instruction");
+ }
+
+ public void updateTarget(int targetIndex, int targetAddressOffset) {
+ targets[targetIndex] = targetAddressOffset;
+ }
+
+ public Format getFormat() {
+ return Format.SparseSwitchData;
+ }
+
+ public int getTargetCount() {
+ return targets.length;
+ }
+
+ public int[] getTargets() {
+ return targets;
+ }
+
+ public int[] getKeys() {
+ return keys;
+ }
+
+ public static class SparseSwitchTarget {
+ public int key;
+ public int targetAddressOffset;
+ }
+
+ public Iterator<SparseSwitchTarget> iterateKeysAndTargets() {
+ return new Iterator<SparseSwitchTarget>() {
+ final int targetCount = getTargetCount();
+ int i = 0;
+
+ SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget();
+
+ public boolean hasNext() {
+ return i<targetCount;
+ }
+
+ public SparseSwitchTarget next() {
+ sparseSwitchTarget.key = keys[i];
+ sparseSwitchTarget.targetAddressOffset = targets[i];
+ i++;
+ return sparseSwitchTarget;
+ }
+
+ public void remove() {
+ }
+ };
+ }
+
+ private static class Factory implements Instruction.InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ if (opcode != Opcode.NOP) {
+ throw new RuntimeException("The opcode for a SparseSwitchDataPseudoInstruction must be NOP");
+ }
+ return new SparseSwitchDataPseudoInstruction(buffer, bufferIndex);
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnknownInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnknownInstruction.java
new file mode 100644
index 0000000..8c8c187
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnknownInstruction.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Opcode;
+
+public class UnknownInstruction extends Instruction10x {
+ private short originalOpcode;
+
+ public UnknownInstruction(short originalOpcode) {
+ super(Opcode.NOP);
+ this.originalOpcode = originalOpcode;
+ }
+
+ public short getOriginalOpcode() {
+ return originalOpcode;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java
new file mode 100644
index 0000000..00fd515
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedOdexInstruction.java
@@ -0,0 +1,62 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code.Format;
+
+import org.jf.dexlib.Code.Instruction;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+/**
+ * This represents a "fixed" odexed instruction, where the object register is always null and so the correct type
+ * can't be determined. Typically, these are replaced by an equivalent instruction that would have the same
+ * effect (namely, an NPE)
+ */
+public class UnresolvedOdexInstruction extends Instruction {
+ public final Instruction OriginalInstruction;
+ //the register number that holds the (null) reference type that the instruction operates on
+ public final int ObjectRegisterNum;
+
+ public UnresolvedOdexInstruction(Instruction originalInstruction, int objectRegisterNumber) {
+ super(originalInstruction.opcode);
+ this.OriginalInstruction = originalInstruction;
+ this.ObjectRegisterNum = objectRegisterNumber;
+ }
+
+ protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ throw new RuntimeException("Cannot rewrite an instruction that couldn't be deodexed");
+ }
+
+ @Override
+ public int getSize(int codeAddress) {
+ return OriginalInstruction.getSize(codeAddress);
+ }
+
+ public Format getFormat() {
+ return Format.UnresolvedOdexInstruction;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java
new file mode 100644
index 0000000..7bead1d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java
@@ -0,0 +1,71 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.Code.Format.Format;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public abstract class Instruction {
+ public final Opcode opcode;
+
+ /**
+ * Returns the size of this instruction in code blocks, assuming the instruction is located at the given address
+ * @param codeAddress the code address where the instruction is located
+ * @return The size of this instruction in code blocks
+ **/
+ public int getSize(int codeAddress) {
+ return opcode.format.size/2;
+ }
+
+ protected Instruction(Opcode opcode) {
+ this.opcode = opcode;
+ }
+
+ public abstract Format getFormat();
+
+ public int write(AnnotatedOutput out, int currentCodeAddress) {
+ if (out.annotates()) {
+ annotateInstruction(out, currentCodeAddress);
+ }
+ writeInstruction(out, currentCodeAddress);
+ return currentCodeAddress + getSize(currentCodeAddress);
+ }
+
+ protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
+ out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
+ opcode.name + " instruction");
+ }
+
+ protected abstract void writeInstruction(AnnotatedOutput out, int currentCodeAddress);
+
+ public static interface InstructionFactory {
+ public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java
new file mode 100644
index 0000000..142f5e2
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java
@@ -0,0 +1,99 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.Code.Format.*;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.Hex;
+
+public class InstructionIterator {
+ public static void IterateInstructions(DexFile dexFile, byte[] insns, ProcessInstructionDelegate delegate) {
+ int insnsPosition = 0;
+
+ while (insnsPosition < insns.length) {
+ try
+ {
+ short opcodeValue = (short)(insns[insnsPosition] & 0xFF);
+ if (opcodeValue == 0xFF) {
+ opcodeValue = (short)((0xFF << 8) | insns[insnsPosition+1]);
+ }
+
+ Opcode opcode = Opcode.getOpcodeByValue(opcodeValue);
+
+ Instruction instruction = null;
+
+ if (opcode == null) {
+ System.err.println(String.format("unknown opcode encountered - %x. Treating as nop.",
+ (opcodeValue & 0xFFFF)));
+ instruction = new UnknownInstruction(opcodeValue);
+ } else {
+ if (opcode == Opcode.NOP) {
+ byte secondByte = insns[insnsPosition + 1];
+ switch (secondByte) {
+ case 0:
+ {
+ instruction = new Instruction10x(Opcode.NOP, insns, insnsPosition);
+ break;
+ }
+ case 1:
+ {
+ instruction = new PackedSwitchDataPseudoInstruction(insns, insnsPosition);
+ break;
+ }
+ case 2:
+ {
+ instruction = new SparseSwitchDataPseudoInstruction(insns, insnsPosition);
+ break;
+ }
+ case 3:
+ {
+ instruction = new ArrayDataPseudoInstruction(insns, insnsPosition);
+ break;
+ }
+ }
+ } else {
+ instruction = opcode.format.Factory.makeInstruction(dexFile, opcode, insns, insnsPosition);
+ }
+ }
+
+ assert instruction != null;
+
+ delegate.ProcessInstruction(insnsPosition/2, instruction);
+ insnsPosition += instruction.getSize(insnsPosition/2)*2;
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error occured at code address " + insnsPosition * 2);
+ }
+ }
+ }
+
+ public static interface ProcessInstructionDelegate {
+ public void ProcessInstruction(int codeAddress, Instruction instruction);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java
new file mode 100644
index 0000000..b2e26c0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java
@@ -0,0 +1,126 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.*;
+import org.jf.dexlib.Util.NumberUtils;
+
+public abstract class InstructionWithReference extends Instruction {
+ private Item referencedItem;
+ private ReferenceType referenceType;
+
+ protected InstructionWithReference(Opcode opcode, Item referencedItem) {
+ super(opcode);
+ this.referencedItem = referencedItem;
+ this.referenceType = opcode.referenceType;
+ checkReferenceType();
+ }
+
+ protected InstructionWithReference(Opcode opcode, Item referencedItem, ReferenceType referenceType) {
+ super(opcode);
+ this.referencedItem = referencedItem;
+ this.referenceType = referenceType;
+ checkReferenceType();
+ }
+
+ protected InstructionWithReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
+ super(opcode);
+
+ this.referenceType = readReferenceType(opcode, buffer, bufferIndex);
+ int itemIndex = getReferencedItemIndex(buffer, bufferIndex);
+ lookupReferencedItem(dexFile, opcode, itemIndex);
+ }
+
+ protected int getReferencedItemIndex(byte[] buffer, int bufferIndex) {
+ return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
+ }
+
+ public ReferenceType getReferenceType() {
+ return referenceType;
+ }
+
+ public Item getReferencedItem() {
+ return referencedItem;
+ }
+
+ protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) {
+ return opcode.referenceType;
+ }
+
+ private void lookupReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) {
+ switch (referenceType) {
+ case field:
+ referencedItem = dexFile.FieldIdsSection.getItemByIndex(itemIndex);
+ return;
+ case method:
+ referencedItem = dexFile.MethodIdsSection.getItemByIndex(itemIndex);
+ return;
+ case type:
+ referencedItem = dexFile.TypeIdsSection.getItemByIndex(itemIndex);
+ return;
+ case string:
+ referencedItem = dexFile.StringIdsSection.getItemByIndex(itemIndex);
+ }
+ }
+
+
+ private void checkReferenceType() {
+ switch (referenceType) {
+ case field:
+ if (!(referencedItem instanceof FieldIdItem)) {
+ throw new RuntimeException(referencedItem.getClass().getSimpleName() +
+ " is the wrong item type for opcode " + opcode.name + ". Expecting FieldIdItem.");
+ }
+ return;
+ case method:
+ if (!(referencedItem instanceof MethodIdItem)) {
+ throw new RuntimeException(referencedItem.getClass().getSimpleName() +
+ " is the wrong item type for opcode " + opcode.name + ". Expecting MethodIdItem.");
+ }
+ return;
+ case type:
+ if (!(referencedItem instanceof TypeIdItem)) {
+ throw new RuntimeException(referencedItem.getClass().getSimpleName() +
+ " is the wrong item type for opcode " + opcode.name + ". Expecting TypeIdItem.");
+ }
+ return;
+ case string:
+ if (!(referencedItem instanceof StringIdItem)) {
+ throw new RuntimeException(referencedItem.getClass().getSimpleName() +
+ " is the wrong item type for opcode " + opcode.name + ". Expecting StringIdItem.");
+ }
+ return;
+ default:
+ if (referencedItem != null) {
+ throw new RuntimeException(referencedItem.getClass().getSimpleName() +
+ " is invalid for opcode " + opcode.name + ". This opcode does not reference an item");
+ }
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InvokeInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/InvokeInstruction.java
new file mode 100644
index 0000000..4710dce
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/InvokeInstruction.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface InvokeInstruction {
+ int getRegCount();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/LiteralInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/LiteralInstruction.java
new file mode 100644
index 0000000..17bfc63
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/LiteralInstruction.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface LiteralInstruction {
+ long getLiteral();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java
new file mode 100644
index 0000000..79d3087
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java
@@ -0,0 +1,34 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface MultiOffsetInstruction {
+ public int[] getTargets();
+ public void updateTarget(int targetIndex, int targetAddressOffset);
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/OdexedFieldAccess.java b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedFieldAccess.java
new file mode 100644
index 0000000..58f03fd
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedFieldAccess.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface OdexedFieldAccess {
+ int getFieldOffset();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeInline.java b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeInline.java
new file mode 100644
index 0000000..639d740
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeInline.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface OdexedInvokeInline extends InvokeInstruction {
+ int getInlineIndex();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeVirtual.java b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeVirtual.java
new file mode 100644
index 0000000..02fd1e6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/OdexedInvokeVirtual.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface OdexedInvokeVirtual {
+ int getVtableIndex();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java
new file mode 100644
index 0000000..d0589c7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java
@@ -0,0 +1,38 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public abstract class OffsetInstruction extends Instruction {
+ protected OffsetInstruction(Opcode opcode) {
+ super(opcode);
+ }
+
+ public abstract int getTargetAddressOffset();
+ public abstract void updateTargetAddressOffset(int targetAddressOffset);
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java
new file mode 100644
index 0000000..6e4806e
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java
@@ -0,0 +1,540 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.Code.Format.Format;
+
+import java.util.HashMap;
+
+public enum Opcode
+{
+ NOP((short)0x00, "nop", ReferenceType.none, Format.Format10x, Opcode.CAN_CONTINUE),
+ MOVE((short)0x01, "move", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_FROM16((short)0x02, "move/from16", ReferenceType.none, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_16((short)0x03, "move/16", ReferenceType.none, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_WIDE((short)0x04, "move-wide", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MOVE_WIDE_FROM16((short)0x05, "move-wide/from16", ReferenceType.none, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MOVE_WIDE_16((short)0x06, "move-wide/16", ReferenceType.none, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MOVE_OBJECT((short)0x07, "move-object", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_OBJECT_FROM16((short)0x08, "move-object/from16", ReferenceType.none, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_OBJECT_16((short)0x09, "move-object/16", ReferenceType.none, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_RESULT((short)0x0a, "move-result", ReferenceType.none, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_RESULT_WIDE((short)0x0b, "move-result-wide", ReferenceType.none, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MOVE_RESULT_OBJECT((short)0x0c, "move-result-object", ReferenceType.none, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MOVE_EXCEPTION((short)0x0d, "move-exception", ReferenceType.none, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ RETURN_VOID((short)0x0e, "return-void", ReferenceType.none, Format.Format10x),
+ RETURN((short)0x0f, "return", ReferenceType.none, Format.Format11x),
+ RETURN_WIDE((short)0x10, "return-wide", ReferenceType.none, Format.Format11x),
+ RETURN_OBJECT((short)0x11, "return-object", ReferenceType.none, Format.Format11x),
+ CONST_4((short)0x12, "const/4", ReferenceType.none, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CONST_16((short)0x13, "const/16", ReferenceType.none, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CONST((short)0x14, "const", ReferenceType.none, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CONST_HIGH16((short)0x15, "const/high16", ReferenceType.none, Format.Format21h, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CONST_WIDE_16((short)0x16, "const-wide/16", ReferenceType.none, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ CONST_WIDE_32((short)0x17, "const-wide/32", ReferenceType.none, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ CONST_WIDE((short)0x18, "const-wide", ReferenceType.none, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ CONST_WIDE_HIGH16((short)0x19, "const-wide/high16", ReferenceType.none, Format.Format21h, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ CONST_STRING((short)0x1a, "const-string", ReferenceType.string, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0x1b),
+ CONST_STRING_JUMBO((short)0x1b, "const-string/jumbo", ReferenceType.string, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CONST_CLASS((short)0x1c, "const-class", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff00),
+ MONITOR_ENTER((short)0x1d, "monitor-enter", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ MONITOR_EXIT((short)0x1e, "monitor-exit", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ CHECK_CAST((short)0x1f, "check-cast", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff01),
+ INSTANCE_OF((short)0x20, "instance-of", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff02),
+ ARRAY_LENGTH((short)0x21, "array-length", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.type, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff03),
+ NEW_ARRAY((short)0x23, "new-array", ReferenceType.type, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff04),
+ FILLED_NEW_ARRAY((short)0x24, "filled-new-array", ReferenceType.type, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.type, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff05),
+ FILL_ARRAY_DATA((short)0x26, "fill-array-data", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
+ THROW((short)0x27, "throw", ReferenceType.none, Format.Format11x, Opcode.CAN_THROW),
+ GOTO((short)0x28, "goto", ReferenceType.none, Format.Format10t),
+ GOTO_16((short)0x29, "goto/16", ReferenceType.none, Format.Format20t),
+ GOTO_32((short)0x2a, "goto/32", ReferenceType.none, Format.Format30t),
+ PACKED_SWITCH((short)0x2b, "packed-switch", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
+ SPARSE_SWITCH((short)0x2c, "sparse-switch", ReferenceType.none, Format.Format31t, Opcode.CAN_CONTINUE),
+ CMPL_FLOAT((short)0x2d, "cmpl-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CMPG_FLOAT((short)0x2e, "cmpg-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CMPL_DOUBLE((short)0x2f, "cmpl-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CMPG_DOUBLE((short)0x30, "cmpg-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ CMP_LONG((short)0x31, "cmp-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IF_EQ((short)0x32, "if-eq", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_NE((short)0x33, "if-ne", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_LT((short)0x34, "if-lt", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_GE((short)0x35, "if-ge", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_GT((short)0x36, "if-gt", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_LE((short)0x37, "if-le", ReferenceType.none, Format.Format22t, Opcode.CAN_CONTINUE),
+ IF_EQZ((short)0x38, "if-eqz", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ IF_NEZ((short)0x39, "if-nez", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ IF_LTZ((short)0x3a, "if-ltz", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ IF_GEZ((short)0x3b, "if-gez", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ IF_GTZ((short)0x3c, "if-gtz", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ IF_LEZ((short)0x3d, "if-lez", ReferenceType.none, Format.Format21t, Opcode.CAN_CONTINUE),
+ AGET((short)0x44, "aget", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AGET_WIDE((short)0x45, "aget-wide", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ AGET_OBJECT((short)0x46, "aget-object", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AGET_BOOLEAN((short)0x47, "aget-boolean", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AGET_BYTE((short)0x48, "aget-byte", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AGET_CHAR((short)0x49, "aget-char", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AGET_SHORT((short)0x4a, "aget-short", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ APUT((short)0x4b, "aput", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_WIDE((short)0x4c, "aput-wide", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_OBJECT((short)0x4d, "aput-object", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_BOOLEAN((short)0x4e, "aput-boolean", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_BYTE((short)0x4f, "aput-byte", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_CHAR((short)0x50, "aput-char", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ APUT_SHORT((short)0x51, "aput-short", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IGET((short)0x52, "iget", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff06),
+ IGET_WIDE((short)0x53, "iget-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER, (short)0xff07),
+ IGET_OBJECT((short)0x54, "iget-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff08),
+ IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff09),
+ IGET_BYTE((short)0x56, "iget-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0a),
+ IGET_CHAR((short)0x57, "iget-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0b),
+ IGET_SHORT((short)0x58, "iget-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff0c),
+ IPUT((short)0x59, "iput", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0d),
+ IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0e),
+ IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff0f),
+ IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff10),
+ IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff11),
+ IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff12),
+ IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.field, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff13),
+ SGET((short)0x60, "sget", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff14),
+ SGET_WIDE((short)0x61, "sget-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER, (short)0xff15),
+ SGET_OBJECT((short)0x62, "sget-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff16),
+ SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff17),
+ SGET_BYTE((short)0x64, "sget-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff18),
+ SGET_CHAR((short)0x65, "sget-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff19),
+ SGET_SHORT((short)0x66, "sget-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0xff1a),
+ SPUT((short)0x67, "sput", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1b),
+ SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1c),
+ SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1d),
+ SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1e),
+ SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff1f),
+ SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff20),
+ SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.field, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE, (short)0xff21),
+ INVOKE_VIRTUAL((short)0x6e, "invoke-virtual", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_SUPER((short)0x6f, "invoke-super", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_DIRECT((short)0x70, "invoke-direct", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+ INVOKE_STATIC((short)0x71, "invoke-static", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_INTERFACE((short)0x72, "invoke-interface", ReferenceType.method, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff22),
+ INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff23),
+ INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE, (short)0xff24),
+ INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff25),
+ INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.method, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT, (short)0xff26),
+ NEG_INT((short)0x7b, "neg-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ NOT_INT((short)0x7c, "not-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ NEG_LONG((short)0x7d, "neg-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ NOT_LONG((short)0x7e, "not-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ NEG_FLOAT((short)0x7f, "neg-float", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ NEG_DOUBLE((short)0x80, "neg-double", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ INT_TO_LONG((short)0x81, "int-to-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ INT_TO_FLOAT((short)0x82, "int-to-float", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ INT_TO_DOUBLE((short)0x83, "int-to-double", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ LONG_TO_INT((short)0x84, "long-to-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ LONG_TO_FLOAT((short)0x85, "long-to-float", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ LONG_TO_DOUBLE((short)0x86, "long-to-double", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ FLOAT_TO_INT((short)0x87, "float-to-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ FLOAT_TO_LONG((short)0x88, "float-to-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ FLOAT_TO_DOUBLE((short)0x89, "float-to-double", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DOUBLE_TO_INT((short)0x8a, "double-to-int", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DOUBLE_TO_LONG((short)0x8b, "double-to-long", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DOUBLE_TO_FLOAT((short)0x8c, "double-to-float", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ INT_TO_BYTE((short)0x8d, "int-to-byte", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ INT_TO_CHAR((short)0x8e, "int-to-char", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ INT_TO_SHORT((short)0x8f, "int-to-short", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_INT((short)0x90, "add-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SUB_INT((short)0x91, "sub-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_INT((short)0x92, "mul-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_INT((short)0x93, "div-int", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_INT((short)0x94, "rem-int", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AND_INT((short)0x95, "and-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ OR_INT((short)0x96, "or-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ XOR_INT((short)0x97, "xor-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHL_INT((short)0x98, "shl-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHR_INT((short)0x99, "shr-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ USHR_INT((short)0x9a, "ushr-int", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_LONG((short)0x9b, "add-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SUB_LONG((short)0x9c, "sub-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MUL_LONG((short)0x9d, "mul-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DIV_LONG((short)0x9e, "div-long", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ REM_LONG((short)0x9f, "rem-long", ReferenceType.none, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ AND_LONG((short)0xa0, "and-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ OR_LONG((short)0xa1, "or-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ XOR_LONG((short)0xa2, "xor-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SHL_LONG((short)0xa3, "shl-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SHR_LONG((short)0xa4, "shr-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ USHR_LONG((short)0xa5, "ushr-long", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ ADD_FLOAT((short)0xa6, "add-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SUB_FLOAT((short)0xa7, "sub-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_FLOAT((short)0xa8, "mul-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_FLOAT((short)0xa9, "div-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_FLOAT((short)0xaa, "rem-float", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_DOUBLE((short)0xab, "add-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SUB_DOUBLE((short)0xac, "sub-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MUL_DOUBLE((short)0xad, "mul-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DIV_DOUBLE((short)0xae, "div-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ REM_DOUBLE((short)0xaf, "rem-double", ReferenceType.none, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ ADD_INT_2ADDR((short)0xb0, "add-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SUB_INT_2ADDR((short)0xb1, "sub-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_INT_2ADDR((short)0xb2, "mul-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_INT_2ADDR((short)0xb3, "div-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_INT_2ADDR((short)0xb4, "rem-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AND_INT_2ADDR((short)0xb5, "and-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ OR_INT_2ADDR((short)0xb6, "or-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ XOR_INT_2ADDR((short)0xb7, "xor-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHL_INT_2ADDR((short)0xb8, "shl-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHR_INT_2ADDR((short)0xb9, "shr-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ USHR_INT_2ADDR((short)0xba, "ushr-int/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_LONG_2ADDR((short)0xbb, "add-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SUB_LONG_2ADDR((short)0xbc, "sub-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MUL_LONG_2ADDR((short)0xbd, "mul-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DIV_LONG_2ADDR((short)0xbe, "div-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ REM_LONG_2ADDR((short)0xbf, "rem-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ AND_LONG_2ADDR((short)0xc0, "and-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ OR_LONG_2ADDR((short)0xc1, "or-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ XOR_LONG_2ADDR((short)0xc2, "xor-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SHL_LONG_2ADDR((short)0xc3, "shl-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SHR_LONG_2ADDR((short)0xc4, "shr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ USHR_LONG_2ADDR((short)0xc5, "ushr-long/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ ADD_FLOAT_2ADDR((short)0xc6, "add-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SUB_FLOAT_2ADDR((short)0xc7, "sub-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_FLOAT_2ADDR((short)0xc8, "mul-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_FLOAT_2ADDR((short)0xc9, "div-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_FLOAT_2ADDR((short)0xca, "rem-float/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_DOUBLE_2ADDR((short)0xcb, "add-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SUB_DOUBLE_2ADDR((short)0xcc, "sub-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ MUL_DOUBLE_2ADDR((short)0xcd, "mul-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ DIV_DOUBLE_2ADDR((short)0xce, "div-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ REM_DOUBLE_2ADDR((short)0xcf, "rem-double/2addr", ReferenceType.none, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ ADD_INT_LIT16((short)0xd0, "add-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ RSUB_INT((short)0xd1, "rsub-int", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_INT_LIT16((short)0xd2, "mul-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_INT_LIT16((short)0xd3, "div-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_INT_LIT16((short)0xd4, "rem-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AND_INT_LIT16((short)0xd5, "and-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ OR_INT_LIT16((short)0xd6, "or-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ XOR_INT_LIT16((short)0xd7, "xor-int/lit16", ReferenceType.none, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ ADD_INT_LIT8((short)0xd8, "add-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ RSUB_INT_LIT8((short)0xd9, "rsub-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ MUL_INT_LIT8((short)0xda, "mul-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ DIV_INT_LIT8((short)0xdb, "div-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ REM_INT_LIT8((short)0xdc, "rem-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ AND_INT_LIT8((short)0xdd, "and-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ OR_INT_LIT8((short)0xde, "or-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ XOR_INT_LIT8((short)0xdf, "xor-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHL_INT_LIT8((short)0xe0, "shl-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SHR_INT_LIT8((short)0xe1, "shr-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ USHR_INT_LIT8((short)0xe2, "ushr-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+
+ IGET_VOLATILE((short)0xe3, "iget-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IPUT_VOLATILE((short)0xe4, "iput-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_VOLATILE((short)0xe5, "sget-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SPUT_VOLATILE((short)0xe6, "sput-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IGET_OBJECT_VOLATILE((short)0xe7, "iget-object-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IGET_WIDE_VOLATILE((short)0xe8, "iget-wide-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ IPUT_WIDE_VOLATILE((short)0xe9, "iput-wide-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_WIDE_VOLATILE((short)0xea, "sget-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ SPUT_WIDE_VOLATILE((short)0xeb, "sput-wide-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+
+ THROW_VERIFICATION_ERROR((short)0xed, "throw-verification-error", ReferenceType.none, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
+ EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.none, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rmi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", ReferenceType.method, Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+ INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", ReferenceType.method, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+ RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", ReferenceType.none, Format.Format10x, Opcode.ODEX_ONLY),
+ IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IPUT_QUICK((short)0xf5, "iput-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+ INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+
+ IPUT_OBJECT_VOLATILE((short)0xfc, "iput-object-volatile", ReferenceType.field, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_OBJECT_VOLATILE((short)0xfd, "sget-object-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", ReferenceType.field, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+
+ CONST_CLASS_JUMBO((short)0xff00, "const-class/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ CHECK_CAST_JUMBO((short)0xff01, "check-cast/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ INSTANCE_OF_JUMBO((short)0xff02, "instance-of/jumbo", ReferenceType.type, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ NEW_INSTANCE_JUMBO((short)0xff03, "new-instance/jumbo", ReferenceType.type, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ NEW_ARRAY_JUMBO((short)0xff04, "new-array/jumbo", ReferenceType.type, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ FILLED_NEW_ARRAY_JUMBO((short)0xff05, "filled-new-array/jumbo", ReferenceType.type, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
+ IGET_JUMBO((short)0xff06, "iget/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_WIDE_JUMBO((short)0xff07, "iget-wide/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_OBJECT_JUMBO((short)0xff08, "iget-object/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_BOOLEAN_JUMBO((short)0xff09, "iget-boolean/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_BYTE_JUMBO((short)0xff0a, "iget-byte/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_CHAR_JUMBO((short)0xff0b, "iget-char/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_SHORT_JUMBO((short)0xff0c, "iget-short/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IPUT_JUMBO((short)0xff0d, "iput/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_WIDE_JUMBO((short)0xff0e, "iput-wide/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_OBJECT_JUMBO((short)0xff0f, "iput-object/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_BOOLEAN_JUMBO((short)0xff10, "iput-boolean/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_BYTE_JUMBO((short)0xff11, "iput-byte/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_CHAR_JUMBO((short)0xff12, "iput-char/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_SHORT_JUMBO((short)0xff13, "iput-short/jumbo", ReferenceType.field, Format.Format52c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SGET_JUMBO((short)0xff14, "sget/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_WIDE_JUMBO((short)0xff15, "sget-wide/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_OBJECT_JUMBO((short)0xff16, "sget-object/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_BOOLEAN_JUMBO((short)0xff17, "sget-boolean/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_BYTE_JUMBO((short)0xff18, "sget-byte/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_CHAR_JUMBO((short)0xff19, "sget-char/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_SHORT_JUMBO((short)0xff1a, "sget-short/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SPUT_JUMBO((short)0xff1b, "sput/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_WIDE_JUMBO((short)0xff1c, "sput-wide/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_OBJECT_JUMBO((short)0xff1d, "sput-object/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_BOOLEAN_JUMBO((short)0xff1e, "sput-boolean/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_BYTE_JUMBO((short)0xff1f, "sput-byte/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_CHAR_JUMBO((short)0xff20, "sput-char/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_SHORT_JUMBO((short)0xff21, "sput-short/jumbo", ReferenceType.field, Format.Format41c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ INVOKE_VIRTUAL_JUMBO((short)0xff22, "invoke-virtual/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
+ INVOKE_SUPER_JUMBO((short)0xff23, "invoke-super/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
+ INVOKE_DIRECT_JUMBO((short)0xff24, "invoke-direct/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE | Opcode.CAN_INITIALIZE_REFERENCE),
+ INVOKE_STATIC_JUMBO((short)0xff25, "invoke-static/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
+ INVOKE_INTERFACE_JUMBO((short)0xff26, "invoke-interface/jumbo", ReferenceType.method, Format.Format5rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE),
+
+ INVOKE_OBJECT_INIT_JUMBO((short)0xfff2, "invoke-object-init/jumbo", ReferenceType.method, Format.Format5rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.JUMBO_OPCODE | Opcode.CAN_INITIALIZE_REFERENCE),
+ IGET_VOLATILE_JUMBO((short)0xfff3, "iget-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_WIDE_VOLATILE_JUMBO((short)0xfff4, "iget-wide-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
+ IGET_OBJECT_VOLATILE_JUMBO((short)0xfff5, "iget-object-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ IPUT_VOLATILE_JUMBO((short)0xfff6, "iput-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_WIDE_VOLATILE_JUMBO((short)0xfff7, "iput-wide-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ IPUT_OBJECT_VOLATILE_JUMBO((short)0xfff8, "iput-object-volatile/jumbo", ReferenceType.field, Format.Format52c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SGET_VOLATILE_JUMBO((short)0xfff9, "sget-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_WIDE_VOLATILE_JUMBO((short)0xfffa, "sget-wide-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.JUMBO_OPCODE),
+ SGET_OBJECT_VOLATILE_JUMBO((short)0xfffb, "sget-object-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.JUMBO_OPCODE),
+ SPUT_VOLATILE_JUMBO((short)0xfffc, "sput-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_WIDE_VOLATILE_JUMBO((short)0xfffd, "sput-wide-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE),
+ SPUT_OBJECT_VOLATILE_JUMBO((short)0xfffe, "sput-object-volatile/jumbo", ReferenceType.field, Format.Format41c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.JUMBO_OPCODE);
+
+ private static Opcode[] opcodesByValue;
+ private static Opcode[] expandedOpcodesByValue;
+ private static HashMap<Integer, Opcode> opcodesByName;
+
+ //if the instruction can throw an exception
+ public static final int CAN_THROW = 0x1;
+ //if the instruction is an odex only instruction
+ public static final int ODEX_ONLY = 0x2;
+ //if execution can continue to the next instruction
+ public static final int CAN_CONTINUE = 0x4;
+ //if the instruction sets the "hidden" result register
+ public static final int SETS_RESULT = 0x8;
+ //if the instruction sets the value of it's first register
+ public static final int SETS_REGISTER = 0x10;
+ //if the instruction sets the value of it's first register to a wide type
+ public static final int SETS_WIDE_REGISTER = 0x20;
+ //if the instruction is an odexed iget-quick/iput-quick instruction
+ public static final int ODEXED_INSTANCE_QUICK = 0x40;
+ //if the instruction is an odexed iget-volatile/iput-volatile instruction
+ public static final int ODEXED_INSTANCE_VOLATILE = 0x80;
+ //if the instruction is an odexed sget-volatile/sput-volatile instruction
+ public static final int ODEXED_STATIC_VOLATILE = 0x100;
+ //if the instruction is a jumbo instruction
+ public static final int JUMBO_OPCODE = 0x200;
+ //if the instruction can initialize an uninitialized object reference
+ public static final int CAN_INITIALIZE_REFERENCE = 0x400;
+
+ static {
+ opcodesByValue = new Opcode[256];
+ expandedOpcodesByValue = new Opcode[256];
+ opcodesByName = new HashMap<Integer, Opcode>();
+
+ for (Opcode opcode: Opcode.values()) {
+ //INVOKE_DIRECT_EMPTY was changed to INVOKE_OBJECT_INIT_RANGE in ICS
+ if (opcode != INVOKE_DIRECT_EMPTY) {
+ if (((opcode.value >> 8) & 0xFF) == 0x00) {
+ opcodesByValue[opcode.value & 0xFF] = opcode;
+ } else {
+ assert ((opcode.value >> 8) & 0xFF) == 0xFF;
+ expandedOpcodesByValue[opcode.value & 0xFF] = opcode;
+ }
+ opcodesByName.put(opcode.name.hashCode(), opcode);
+ }
+ }
+ }
+
+ public static Opcode getOpcodeByName(String opcodeName) {
+ return opcodesByName.get(opcodeName.toLowerCase().hashCode());
+ }
+
+ public static Opcode getOpcodeByValue(short opcodeValue) {
+ if (((opcodeValue >> 8) & 0xFF) == 0x00) {
+ return opcodesByValue[opcodeValue & 0xFF];
+ } else {
+ assert ((opcodeValue >> 8) & 0xFF) == 0xFF;
+ return expandedOpcodesByValue[opcodeValue & 0xFF];
+ }
+ }
+
+ private static void removeOpcodes(Opcode... toRemove) {
+ for (Opcode opcode: toRemove) {
+ opcodesByName.remove(opcode.name.toLowerCase().hashCode());
+
+ if (((opcode.value >> 8) & 0xFF) == 0x00) {
+ opcodesByValue[opcode.value] = null;
+ } else {
+ expandedOpcodesByValue[opcode.value & 0xFF] = null;
+ }
+ }
+ }
+
+ private static void addOpcodes(Opcode... toAdd) {
+ for (Opcode opcode: toAdd) {
+ if (((opcode.value >> 8) & 0xFF) == 0x00) {
+ opcodesByValue[opcode.value & 0xFF] = opcode;
+ } else {
+ assert ((opcode.value >> 8) & 0xFF) == 0xFF;
+ expandedOpcodesByValue[opcode.value & 0xFF] = opcode;
+ }
+ opcodesByName.put(opcode.name.hashCode(), opcode);
+ }
+ }
+
+ /**
+ * This will add/remove/replace various opcodes in the value/name maps as needed,
+ * based on the idiosyncrasies of that api level
+ * @param apiLevel
+ */
+ public static void updateMapsForApiLevel(int apiLevel, boolean includeJumbo) {
+ if (apiLevel < 5) {
+ removeOpcodes(THROW_VERIFICATION_ERROR);
+ }
+ if (apiLevel < 8) {
+ removeOpcodes(EXECUTE_INLINE_RANGE);
+ }
+ if (apiLevel < 9) {
+ removeOpcodes(IGET_VOLATILE, IPUT_VOLATILE, SGET_VOLATILE, SPUT_VOLATILE, IGET_OBJECT_VOLATILE,
+ IGET_WIDE_VOLATILE, IPUT_WIDE_VOLATILE, SGET_WIDE_VOLATILE, SPUT_WIDE_VOLATILE,
+ IPUT_OBJECT_VOLATILE, SGET_OBJECT_VOLATILE, SPUT_OBJECT_VOLATILE);
+ }
+ if (apiLevel < 11) {
+ removeOpcodes(RETURN_VOID_BARRIER);
+ }
+ if (apiLevel < 14) {
+ removeOpcodes(INVOKE_OBJECT_INIT_RANGE);
+ addOpcodes(INVOKE_DIRECT_EMPTY);
+ }
+ if (apiLevel < 14 || !includeJumbo) {
+ removeOpcodes(CONST_CLASS_JUMBO, CHECK_CAST_JUMBO, INSTANCE_OF_JUMBO, NEW_INSTANCE_JUMBO,
+ NEW_ARRAY_JUMBO, FILLED_NEW_ARRAY_JUMBO, IGET_JUMBO, IGET_WIDE_JUMBO, IGET_OBJECT_JUMBO,
+ IGET_BOOLEAN_JUMBO, IGET_BYTE_JUMBO, IGET_CHAR_JUMBO, IGET_SHORT_JUMBO, IPUT_JUMBO, IPUT_WIDE_JUMBO,
+ IPUT_OBJECT_JUMBO, IPUT_BOOLEAN_JUMBO, IPUT_BYTE_JUMBO, IPUT_CHAR_JUMBO, IPUT_SHORT_JUMBO,
+ SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO,
+ SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO,
+ SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO,
+ INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO,
+ INVOKE_OBJECT_INIT_JUMBO, IGET_VOLATILE_JUMBO, IGET_WIDE_VOLATILE_JUMBO,
+ IGET_OBJECT_VOLATILE_JUMBO, IPUT_VOLATILE_JUMBO, IPUT_WIDE_VOLATILE_JUMBO,
+ IPUT_OBJECT_VOLATILE_JUMBO, SGET_VOLATILE_JUMBO, SGET_WIDE_VOLATILE_JUMBO,
+ SGET_OBJECT_VOLATILE_JUMBO, SPUT_VOLATILE_JUMBO, SPUT_WIDE_VOLATILE_JUMBO,
+ SPUT_OBJECT_VOLATILE_JUMBO);
+ }
+ }
+
+ public final short value;
+ public final String name;
+ public final ReferenceType referenceType;
+ public final Format format;
+ public final int flags;
+ private final short jumboOpcode;
+
+ Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format) {
+ this(opcodeValue, opcodeName, referenceType, format, 0);
+ }
+
+ Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags) {
+ this(opcodeValue, opcodeName, referenceType, format, flags, (short)-1);
+ }
+
+ Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags, short jumboOpcodeValue) {
+ this.value = opcodeValue;
+ this.name = opcodeName;
+ this.referenceType = referenceType;
+ this.format = format;
+ this.flags = flags;
+ this.jumboOpcode = jumboOpcodeValue;
+ }
+
+ public final boolean canThrow() {
+ return (flags & CAN_THROW) != 0;
+ }
+
+ public final boolean odexOnly() {
+ return (flags & ODEX_ONLY) != 0;
+ }
+
+ public final boolean canContinue() {
+ return (flags & CAN_CONTINUE) != 0;
+ }
+
+ public final boolean setsResult() {
+ return (flags & SETS_RESULT) != 0;
+ }
+
+ public final boolean setsRegister() {
+ return (flags & SETS_REGISTER) != 0;
+ }
+
+ public final boolean setsWideRegister() {
+ return (flags & SETS_WIDE_REGISTER) != 0;
+ }
+
+ public final boolean isOdexedInstanceQuick() {
+ return (flags & ODEXED_INSTANCE_QUICK) != 0;
+ }
+
+ public final boolean isOdexedInstanceVolatile() {
+ return (flags & ODEXED_INSTANCE_VOLATILE) != 0;
+ }
+
+ public final boolean isOdexedStaticVolatile() {
+ return (flags & ODEXED_STATIC_VOLATILE) != 0;
+ }
+
+ public final boolean isJumboOpcode() {
+ return (flags & JUMBO_OPCODE) != 0;
+ }
+
+ public final boolean canInitializeReference() {
+ return (flags & CAN_INITIALIZE_REFERENCE) != 0;
+ }
+
+ public final boolean hasJumboOpcode() {
+ return jumboOpcode != -1 && Opcode.getOpcodeByValue(jumboOpcode) != null;
+ }
+
+ public final Opcode getJumboOpcode() {
+ return Opcode.getOpcodeByValue(jumboOpcode);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java
new file mode 100644
index 0000000..f6d147a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java
@@ -0,0 +1,80 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import org.jf.dexlib.*;
+
+public enum ReferenceType
+{
+ string(-1),
+ type(0),
+ field(1),
+ method(2),
+ none(-1);
+
+ private int validationErrorReferenceType;
+
+ private ReferenceType(int validationErrorReferenceType) {
+ this.validationErrorReferenceType = validationErrorReferenceType;
+ }
+
+ public boolean checkItem(Item item) {
+ switch (this) {
+ case string:
+ return item instanceof StringIdItem;
+ case type:
+ return item instanceof TypeIdItem;
+ case field:
+ return item instanceof FieldIdItem;
+ case method:
+ return item instanceof MethodIdItem;
+ }
+ return false;
+ }
+
+ public static ReferenceType fromValidationErrorReferenceType(int validationErrorReferenceType) {
+ switch (validationErrorReferenceType) {
+ case 0:
+ return type;
+ case 1:
+ return field;
+ case 2:
+ return method;
+ }
+ return null;
+ }
+
+ public int getValidationErrorReferenceType() {
+ if (validationErrorReferenceType == -1) {
+ throw new RuntimeException("This reference type cannot be referenced from a throw-validation-error" +
+ " instruction");
+ }
+ return validationErrorReferenceType;
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java
new file mode 100644
index 0000000..21e3719
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface RegisterRangeInstruction extends InvokeInstruction {
+ int getStartRegister();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java
new file mode 100644
index 0000000..0cc4656
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface SingleRegisterInstruction {
+ int getRegisterA();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java
new file mode 100644
index 0000000..83bbb80
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java
@@ -0,0 +1,33 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface ThreeRegisterInstruction extends TwoRegisterInstruction {
+ int getRegisterC();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java
new file mode 100644
index 0000000..022a145
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java
@@ -0,0 +1,35 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+public interface TwoRegisterInstruction extends SingleRegisterInstruction {
+ int getRegisterA();
+ int getRegisterB();
+
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java b/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java
new file mode 100644
index 0000000..622be5d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java
@@ -0,0 +1,99 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Code;
+
+import java.util.HashMap;
+
+public enum VerificationErrorType {
+ None(0, "no-error"),
+ Generic(1, "generic-error"),
+ NoClass(2, "no-such-class"),
+ NoField(3, "no-such-field"),
+ NoMethod(4, "no-such-method"),
+ AccessClass(5, "illegal-class-access"),
+ AccessField(6, "illegal-field-access"),
+ AccessMethod(7, "illegal-method-access"),
+ ClassChange(8, "class-change-error"),
+ Instantiation(9, "instantiation-error");
+
+ private static HashMap<String, VerificationErrorType> verificationErrorTypesByName;
+
+ static {
+ verificationErrorTypesByName = new HashMap<String, VerificationErrorType>();
+
+ for (VerificationErrorType verificationErrorType: VerificationErrorType.values()) {
+ verificationErrorTypesByName.put(verificationErrorType.getName(), verificationErrorType);
+ }
+ }
+
+ private int value;
+ private String name;
+ private VerificationErrorType(int value, String name) {
+ this.value = value;
+ this.name = name;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static VerificationErrorType fromString(String validationErrorType) {
+ return verificationErrorTypesByName.get(validationErrorType);
+ }
+
+ public static VerificationErrorType getValidationErrorType(int validationErrorType) {
+ switch (validationErrorType) {
+ case 0:
+ return None;
+ case 1:
+ return Generic;
+ case 2:
+ return NoClass;
+ case 3:
+ return NoField;
+ case 4:
+ return NoMethod;
+ case 5:
+ return AccessClass;
+ case 6:
+ return AccessField;
+ case 7:
+ return AccessMethod;
+ case 8:
+ return ClassChange;
+ case 9:
+ return Instantiation;
+ }
+ return null;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java
new file mode 100644
index 0000000..e56bfb5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java
@@ -0,0 +1,1083 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Code.Format.*;
+import org.jf.dexlib.Code.*;
+import org.jf.dexlib.Debug.DebugInstructionIterator;
+import org.jf.dexlib.Debug.DebugOpcode;
+import org.jf.dexlib.Util.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CodeItem extends Item<CodeItem> {
+ private int registerCount;
+ private int inWords;
+ private int outWords;
+ private DebugInfoItem debugInfo;
+ private Instruction[] instructions;
+ private TryItem[] tries;
+ private EncodedCatchHandler[] encodedCatchHandlers;
+
+ private ClassDataItem.EncodedMethod parent;
+
+ /**
+ * Creates a new uninitialized <code>CodeItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ public CodeItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>CodeItem</code> with the given values.
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param registerCount the number of registers that the method containing this code uses
+ * @param inWords the number of 2-byte words that the parameters to the method containing this code take
+ * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
+ * @param debugInfo the debug information for this code/method
+ * @param instructions the instructions for this code item
+ * @param tries an array of the tries defined for this code/method
+ * @param encodedCatchHandlers an array of the exception handlers defined for this code/method
+ */
+ private CodeItem(DexFile dexFile,
+ int registerCount,
+ int inWords,
+ int outWords,
+ DebugInfoItem debugInfo,
+ Instruction[] instructions,
+ TryItem[] tries,
+ EncodedCatchHandler[] encodedCatchHandlers) {
+ super(dexFile);
+
+ this.registerCount = registerCount;
+ this.inWords = inWords;
+ this.outWords = outWords;
+ this.debugInfo = debugInfo;
+ if (debugInfo != null) {
+ debugInfo.setParent(this);
+ }
+
+ this.instructions = instructions;
+ this.tries = tries;
+ this.encodedCatchHandlers = encodedCatchHandlers;
+ }
+
+ /**
+ * Returns a new <code>CodeItem</code> with the given values.
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param registerCount the number of registers that the method containing this code uses
+ * @param inWords the number of 2-byte words that the parameters to the method containing this code take
+ * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
+ * @param debugInfo the debug information for this code/method
+ * @param instructions the instructions for this code item
+ * @param tries a list of the tries defined for this code/method or null if none
+ * @param encodedCatchHandlers a list of the exception handlers defined for this code/method or null if none
+ * @return a new <code>CodeItem</code> with the given values.
+ */
+ public static CodeItem internCodeItem(DexFile dexFile,
+ int registerCount,
+ int inWords,
+ int outWords,
+ DebugInfoItem debugInfo,
+ List<Instruction> instructions,
+ List<TryItem> tries,
+ List<EncodedCatchHandler> encodedCatchHandlers) {
+ TryItem[] triesArray = null;
+ EncodedCatchHandler[] encodedCatchHandlersArray = null;
+ Instruction[] instructionsArray = null;
+
+ if (tries != null && tries.size() > 0) {
+ triesArray = new TryItem[tries.size()];
+ tries.toArray(triesArray);
+ }
+
+ if (encodedCatchHandlers != null && encodedCatchHandlers.size() > 0) {
+ encodedCatchHandlersArray = new EncodedCatchHandler[encodedCatchHandlers.size()];
+ encodedCatchHandlers.toArray(encodedCatchHandlersArray);
+ }
+
+ if (instructions != null && instructions.size() > 0) {
+ instructionsArray = new Instruction[instructions.size()];
+ instructions.toArray(instructionsArray);
+ }
+
+ CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, instructionsArray,
+ triesArray, encodedCatchHandlersArray);
+ return dexFile.CodeItemsSection.intern(codeItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ this.registerCount = in.readShort();
+ this.inWords = in.readShort();
+ this.outWords = in.readShort();
+ int triesCount = in.readShort();
+ this.debugInfo = (DebugInfoItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM,
+ in.readInt());
+ if (this.debugInfo != null) {
+ this.debugInfo.setParent(this);
+ }
+
+ int instructionCount = in.readInt();
+
+ final ArrayList<Instruction> instructionList = new ArrayList<Instruction>();
+
+ byte[] encodedInstructions = in.readBytes(instructionCount * 2);
+ InstructionIterator.IterateInstructions(dexFile, encodedInstructions,
+ new InstructionIterator.ProcessInstructionDelegate() {
+ public void ProcessInstruction(int codeAddress, Instruction instruction) {
+ instructionList.add(instruction);
+ }
+ });
+
+ this.instructions = new Instruction[instructionList.size()];
+ instructionList.toArray(instructions);
+
+ if (triesCount > 0) {
+ in.alignTo(4);
+
+ //we need to read in the catch handlers first, so save the offset to the try items for future reference
+ int triesOffset = in.getCursor();
+ in.setCursor(triesOffset + 8 * triesCount);
+
+ //read in the encoded catch handlers
+ int encodedHandlerStart = in.getCursor();
+ int handlerCount = in.readUnsignedLeb128();
+ SparseArray<EncodedCatchHandler> handlerMap = new SparseArray<EncodedCatchHandler>(handlerCount);
+ encodedCatchHandlers = new EncodedCatchHandler[handlerCount];
+ for (int i=0; i<handlerCount; i++) {
+ try {
+ int position = in.getCursor() - encodedHandlerStart;
+ encodedCatchHandlers[i] = new EncodedCatchHandler(dexFile, in);
+ handlerMap.append(position, encodedCatchHandlers[i]);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading EncodedCatchHandler at index " + i);
+ }
+ }
+ int codeItemEnd = in.getCursor();
+
+ //now go back and read the tries
+ in.setCursor(triesOffset);
+ tries = new TryItem[triesCount];
+ for (int i=0; i<triesCount; i++) {
+ try {
+ tries[i] = new TryItem(in, handlerMap);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading TryItem at index " + i);
+ }
+ }
+
+ //and now back to the end of the code item
+ in.setCursor(codeItemEnd);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ offset += 16 + getInstructionsLength() * 2;
+
+ if (tries != null && tries.length > 0) {
+ offset = AlignmentUtils.alignOffset(offset, 4);
+
+ offset += tries.length * 8;
+ int encodedCatchHandlerBaseOffset = offset;
+ offset += Leb128Utils.unsignedLeb128Size(encodedCatchHandlers.length);
+ for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
+ offset = encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset);
+ }
+ }
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(final AnnotatedOutput out) {
+ int instructionsLength = getInstructionsLength();
+
+ if (out.annotates()) {
+ out.annotate(0, parent.method.getMethodString());
+ out.annotate(2, "registers_size: 0x" + Integer.toHexString(registerCount) + " (" + registerCount + ")");
+ out.annotate(2, "ins_size: 0x" + Integer.toHexString(inWords) + " (" + inWords + ")");
+ out.annotate(2, "outs_size: 0x" + Integer.toHexString(outWords) + " (" + outWords + ")");
+ int triesLength = tries==null?0:tries.length;
+ out.annotate(2, "tries_size: 0x" + Integer.toHexString(triesLength) + " (" + triesLength + ")");
+ if (debugInfo == null) {
+ out.annotate(4, "debug_info_off:");
+ } else {
+ out.annotate(4, "debug_info_off: 0x" + Integer.toHexString(debugInfo.getOffset()));
+ }
+ out.annotate(4, "insns_size: 0x" + Integer.toHexString(instructionsLength) + " (" +
+ (instructionsLength) + ")");
+ }
+
+ out.writeShort(registerCount);
+ out.writeShort(inWords);
+ out.writeShort(outWords);
+ if (tries == null) {
+ out.writeShort(0);
+ } else {
+ out.writeShort(tries.length);
+ }
+ if (debugInfo == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(debugInfo.getOffset());
+ }
+
+ out.writeInt(instructionsLength);
+
+ int currentCodeAddress = 0;
+ for (Instruction instruction: instructions) {
+ currentCodeAddress = instruction.write(out, currentCodeAddress);
+ }
+
+ if (tries != null && tries.length > 0) {
+ if (out.annotates()) {
+ if ((currentCodeAddress % 2) != 0) {
+ out.annotate("padding");
+ out.writeShort(0);
+ }
+
+ int index = 0;
+ for (TryItem tryItem: tries) {
+ out.annotate(0, "[0x" + Integer.toHexString(index++) + "] try_item");
+ out.indent();
+ tryItem.writeTo(out);
+ out.deindent();
+ }
+
+ out.annotate("handler_count: 0x" + Integer.toHexString(encodedCatchHandlers.length) + "(" +
+ encodedCatchHandlers.length + ")");
+ out.writeUnsignedLeb128(encodedCatchHandlers.length);
+
+ index = 0;
+ for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
+ out.annotate(0, "[" + Integer.toHexString(index++) + "] encoded_catch_handler");
+ out.indent();
+ encodedCatchHandler.writeTo(out);
+ out.deindent();
+ }
+ } else {
+ if ((currentCodeAddress % 2) != 0) {
+ out.writeShort(0);
+ }
+
+ for (TryItem tryItem: tries) {
+ tryItem.writeTo(out);
+ }
+
+ out.writeUnsignedLeb128(encodedCatchHandlers.length);
+
+ for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
+ encodedCatchHandler.writeTo(out);
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_CODE_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ if (this.parent == null) {
+ return "code_item @0x" + Integer.toHexString(getOffset());
+ }
+ return "code_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.method.getMethodString() + ")";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(CodeItem other) {
+ if (parent == null) {
+ if (other.parent == null) {
+ return 0;
+ }
+ return -1;
+ }
+ if (other.parent == null) {
+ return 1;
+ }
+ return parent.method.compareTo(other.parent.method);
+ }
+
+ /**
+ * @return the register count
+ */
+ public int getRegisterCount() {
+ return registerCount;
+ }
+
+ /**
+ * @return an array of the instructions in this code item
+ */
+ public Instruction[] getInstructions() {
+ return instructions;
+ }
+
+ /**
+ * @return an array of the <code>TryItem</code> objects in this <code>CodeItem</code>
+ */
+ public TryItem[] getTries() {
+ return tries;
+ }
+
+ /**
+ * @return an array of the <code>EncodedCatchHandler</code> objects in this <code>CodeItem</code>
+ */
+ public EncodedCatchHandler[] getHandlers() {
+ return encodedCatchHandlers;
+ }
+
+ /**
+ * @return the <code>DebugInfoItem</code> associated with this <code>CodeItem</code>
+ */
+ public DebugInfoItem getDebugInfo() {
+ return debugInfo;
+ }
+
+ /**
+ * @return the number of 2-byte words that the parameters to the method containing this code take
+ */
+ public int getInWords() {
+ return inWords;
+ }
+
+ /**
+ * @return the maximum number of 2-byte words for the arguments of any method call in this code
+ */
+ public int getOutWords() {
+ return outWords;
+ }
+
+ /**
+ * Sets the <code>MethodIdItem</code> of the method that this <code>CodeItem</code> is associated with
+ * @param encodedMethod the <code>EncodedMethod</code> of the method that this <code>CodeItem</code> is associated
+ * with
+ */
+ protected void setParent(ClassDataItem.EncodedMethod encodedMethod) {
+ this.parent = encodedMethod;
+ }
+
+ /**
+ * @return the MethodIdItem of the method that this CodeItem belongs to
+ */
+ public ClassDataItem.EncodedMethod getParent() {
+ return parent;
+ }
+
+ /**
+ * Used by OdexUtil to update this <code>CodeItem</code> with a deodexed version of the instructions
+ * @param newInstructions the new instructions to use for this code item
+ */
+ public void updateCode(Instruction[] newInstructions) {
+ this.instructions = newInstructions;
+ }
+
+ /**
+ * @return The length of the instructions in this CodeItem, in 2-byte code blocks
+ */
+ private int getInstructionsLength() {
+ int currentCodeAddress = 0;
+ for (Instruction instruction: instructions) {
+ currentCodeAddress += instruction.getSize(currentCodeAddress);
+ }
+ return currentCodeAddress;
+ }
+
+ /**
+ * Go through the instructions and perform any of the following fixes that are applicable
+ * - Replace const-string instruction with const-string/jumbo, when the string index is too big
+ * - Replace goto and goto/16 with a larger version of goto, when the target is too far away
+ * TODO: we should be able to replace if-* instructions with targets that are too far away with a negated if followed by a goto/32 to the original target
+ * TODO: remove multiple nops that occur before a switch/array data pseudo instruction. In some cases, multiple smali-baksmali cycles with changes in between could cause nops to start piling up
+ * TODO: in case of non-range invoke with a jumbo-sized method reference, we could check if the registers are sequential, and replace it with the jumbo variant (which only takes a register range)
+ *
+ * The above fixes are applied iteratively, until no more fixes have been performed
+ */
+ public void fixInstructions(boolean fixJumbo, boolean fixGoto) {
+ try {
+ boolean didSomething = false;
+
+ do
+ {
+ didSomething = false;
+
+ int currentCodeAddress = 0;
+ for (int i=0; i<instructions.length; i++) {
+ Instruction instruction = instructions[i];
+
+ try {
+ if (fixGoto && instruction.opcode == Opcode.GOTO) {
+ int codeAddress = ((OffsetInstruction)instruction).getTargetAddressOffset();
+
+ if (((byte) codeAddress) != codeAddress) {
+ //the address doesn't fit within a byte, we need to upgrade to a goto/16 or goto/32
+
+ if ((short) codeAddress == codeAddress) {
+ //the address fits in a short, so upgrade to a goto/16
+ replaceInstructionAtAddress(currentCodeAddress,
+ new Instruction20t(Opcode.GOTO_16, codeAddress));
+ }
+ else {
+ //The address won't fit into a short, we have to upgrade to a goto/32
+ replaceInstructionAtAddress(currentCodeAddress,
+ new Instruction30t(Opcode.GOTO_32, codeAddress));
+ }
+ didSomething = true;
+ break;
+ }
+ } else if (fixGoto && instruction.opcode == Opcode.GOTO_16) {
+ int codeAddress = ((OffsetInstruction)instruction).getTargetAddressOffset();
+
+ if (((short) codeAddress) != codeAddress) {
+ //the address doesn't fit within a short, we need to upgrade to a goto/32
+ replaceInstructionAtAddress(currentCodeAddress,
+ new Instruction30t(Opcode.GOTO_32, codeAddress));
+ didSomething = true;
+ break;
+ }
+ } else if (fixJumbo && instruction.opcode.hasJumboOpcode()) {
+ InstructionWithReference referenceInstruction = (InstructionWithReference)instruction;
+ if (referenceInstruction.getReferencedItem().getIndex() > 0xFFFF) {
+
+ InstructionWithJumboVariant instructionWithJumboVariant =
+ (InstructionWithJumboVariant)referenceInstruction;
+
+ Instruction jumboInstruction = instructionWithJumboVariant.makeJumbo();
+ if (jumboInstruction != null) {
+ replaceInstructionAtAddress(currentCodeAddress,
+ instructionWithJumboVariant.makeJumbo());
+ didSomething = true;
+ break;
+ }
+ }
+ }
+
+ currentCodeAddress += instruction.getSize(currentCodeAddress);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while attempting to fix " +
+ instruction.opcode.name + " instruction at address " + currentCodeAddress);
+ }
+ }
+ }while(didSomething);
+ } catch (Exception ex) {
+ throw this.addExceptionContext(ex);
+ }
+ }
+
+ private void replaceInstructionAtAddress(int codeAddress, Instruction replacementInstruction) {
+ Instruction originalInstruction = null;
+
+ int[] originalInstructionCodeAddresses = new int[instructions.length+1];
+ SparseIntArray originalSwitchAddressByOriginalSwitchDataAddress = new SparseIntArray();
+
+ int currentCodeAddress = 0;
+ int instructionIndex = 0;
+ int i;
+ for (i=0; i<instructions.length; i++) {
+ Instruction instruction = instructions[i];
+
+ if (currentCodeAddress == codeAddress) {
+ originalInstruction = instruction;
+ instructionIndex = i;
+ }
+
+ if (instruction.opcode == Opcode.PACKED_SWITCH || instruction.opcode == Opcode.SPARSE_SWITCH) {
+ OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
+
+ int switchDataAddress = currentCodeAddress + offsetInstruction.getTargetAddressOffset();
+ if (originalSwitchAddressByOriginalSwitchDataAddress.indexOfKey(switchDataAddress) < 0) {
+ originalSwitchAddressByOriginalSwitchDataAddress.put(switchDataAddress, currentCodeAddress);
+ }
+ }
+
+ originalInstructionCodeAddresses[i] = currentCodeAddress;
+ currentCodeAddress += instruction.getSize(currentCodeAddress);
+ }
+ //add the address just past the end of the last instruction, to help when fixing up try blocks that end
+ //at the end of the method
+ originalInstructionCodeAddresses[i] = currentCodeAddress;
+
+ if (originalInstruction == null) {
+ throw new RuntimeException("There is no instruction at address " + codeAddress);
+ }
+
+ instructions[instructionIndex] = replacementInstruction;
+
+ //if we're replacing the instruction with one of the same size, we don't have to worry about fixing
+ //up any address
+ if (originalInstruction.getSize(codeAddress) == replacementInstruction.getSize(codeAddress)) {
+ return;
+ }
+
+ final SparseIntArray originalAddressByNewAddress = new SparseIntArray();
+ final SparseIntArray newAddressByOriginalAddress = new SparseIntArray();
+
+ currentCodeAddress = 0;
+ for (i=0; i<instructions.length; i++) {
+ Instruction instruction = instructions[i];
+
+ int originalAddress = originalInstructionCodeAddresses[i];
+ originalAddressByNewAddress.append(currentCodeAddress, originalAddress);
+ newAddressByOriginalAddress.append(originalAddress, currentCodeAddress);
+
+ currentCodeAddress += instruction.getSize(currentCodeAddress);
+ }
+
+ //add the address just past the end of the last instruction, to help when fixing up try blocks that end
+ //at the end of the method
+ originalAddressByNewAddress.append(currentCodeAddress, originalInstructionCodeAddresses[i]);
+ newAddressByOriginalAddress.append(originalInstructionCodeAddresses[i], currentCodeAddress);
+
+ //update any "offset" instructions, or switch data instructions
+ currentCodeAddress = 0;
+ for (i=0; i<instructions.length; i++) {
+ Instruction instruction = instructions[i];
+
+ if (instruction instanceof OffsetInstruction) {
+ OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
+
+ assert originalAddressByNewAddress.indexOfKey(currentCodeAddress) >= 0;
+ int originalAddress = originalAddressByNewAddress.get(currentCodeAddress);
+
+ int originalInstructionTarget = originalAddress + offsetInstruction.getTargetAddressOffset();
+
+ assert newAddressByOriginalAddress.indexOfKey(originalInstructionTarget) >= 0;
+ int newInstructionTarget = newAddressByOriginalAddress.get(originalInstructionTarget);
+
+ int newCodeAddress = (newInstructionTarget - currentCodeAddress);
+
+ if (newCodeAddress != offsetInstruction.getTargetAddressOffset()) {
+ offsetInstruction.updateTargetAddressOffset(newCodeAddress);
+ }
+ } else if (instruction instanceof MultiOffsetInstruction) {
+ MultiOffsetInstruction multiOffsetInstruction = (MultiOffsetInstruction)instruction;
+
+ assert originalAddressByNewAddress.indexOfKey(currentCodeAddress) >= 0;
+ int originalDataAddress = originalAddressByNewAddress.get(currentCodeAddress);
+
+ int originalSwitchAddress =
+ originalSwitchAddressByOriginalSwitchDataAddress.get(originalDataAddress, -1);
+ if (originalSwitchAddress == -1) {
+ //TODO: maybe we could just remove the unreferenced switch data?
+ throw new RuntimeException("This method contains an unreferenced switch data block at address " +
+ + currentCodeAddress + " and can't be automatically fixed.");
+ }
+
+ assert newAddressByOriginalAddress.indexOfKey(originalSwitchAddress) >= 0;
+ int newSwitchAddress = newAddressByOriginalAddress.get(originalSwitchAddress);
+
+ int[] targets = multiOffsetInstruction.getTargets();
+ for (int t=0; t<targets.length; t++) {
+ int originalTargetCodeAddress = originalSwitchAddress + targets[t];
+ assert newAddressByOriginalAddress.indexOfKey(originalTargetCodeAddress) >= 0;
+ int newTargetCodeAddress = newAddressByOriginalAddress.get(originalTargetCodeAddress);
+ int newCodeAddress = newTargetCodeAddress - newSwitchAddress;
+ if (newCodeAddress != targets[t]) {
+ multiOffsetInstruction.updateTarget(t, newCodeAddress);
+ }
+ }
+ }
+ currentCodeAddress += instruction.getSize(currentCodeAddress);
+ }
+
+ if (debugInfo != null) {
+ final byte[] encodedDebugInfo = debugInfo.getEncodedDebugInfo();
+
+ ByteArrayInput debugInput = new ByteArrayInput(encodedDebugInfo);
+
+ DebugInstructionFixer debugInstructionFixer = new DebugInstructionFixer(encodedDebugInfo,
+ newAddressByOriginalAddress);
+ DebugInstructionIterator.IterateInstructions(debugInput, debugInstructionFixer);
+
+ if (debugInstructionFixer.result != null) {
+ debugInfo.setEncodedDebugInfo(debugInstructionFixer.result);
+ }
+ }
+
+ if (encodedCatchHandlers != null) {
+ for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
+ if (encodedCatchHandler.catchAllHandlerAddress != -1) {
+ assert newAddressByOriginalAddress.indexOfKey(encodedCatchHandler.catchAllHandlerAddress) >= 0;
+ encodedCatchHandler.catchAllHandlerAddress =
+ newAddressByOriginalAddress.get(encodedCatchHandler.catchAllHandlerAddress);
+ }
+
+ for (EncodedTypeAddrPair handler: encodedCatchHandler.handlers) {
+ assert newAddressByOriginalAddress.indexOfKey(handler.handlerAddress) >= 0;
+ handler.handlerAddress = newAddressByOriginalAddress.get(handler.handlerAddress);
+ }
+ }
+ }
+
+ if (this.tries != null) {
+ for (TryItem tryItem: tries) {
+ int startAddress = tryItem.startCodeAddress;
+ int endAddress = tryItem.startCodeAddress + tryItem.tryLength;
+
+ assert newAddressByOriginalAddress.indexOfKey(startAddress) >= 0;
+ tryItem.startCodeAddress = newAddressByOriginalAddress.get(startAddress);
+
+ assert newAddressByOriginalAddress.indexOfKey(endAddress) >= 0;
+ tryItem.tryLength = newAddressByOriginalAddress.get(endAddress) - tryItem.startCodeAddress;
+ }
+ }
+ }
+
+ private class DebugInstructionFixer extends DebugInstructionIterator.ProcessRawDebugInstructionDelegate {
+ private int currentCodeAddress = 0;
+ private SparseIntArray newAddressByOriginalAddress;
+ private final byte[] originalEncodedDebugInfo;
+ public byte[] result = null;
+
+ public DebugInstructionFixer(byte[] originalEncodedDebugInfo, SparseIntArray newAddressByOriginalAddress) {
+ this.newAddressByOriginalAddress = newAddressByOriginalAddress;
+ this.originalEncodedDebugInfo = originalEncodedDebugInfo;
+ }
+
+
+ @Override
+ public void ProcessAdvancePC(int startDebugOffset, int debugInstructionLength, int codeAddressDelta) {
+ currentCodeAddress += codeAddressDelta;
+
+ if (result != null) {
+ return;
+ }
+
+ int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1);
+
+ //The address might not point to an actual instruction in some cases, for example, if an AdvancePC
+ //instruction was inserted just before a "special" instruction, to fix up the addresses for a previous
+ //instruction replacement.
+ //In this case, it should be safe to skip, because there will be another AdvancePC/SpecialOpcode that will
+ //bump up the address to point to a valid instruction before anything (line/local/etc.) is emitted
+ if (newCodeAddress == -1) {
+ return;
+ }
+
+ if (newCodeAddress != currentCodeAddress) {
+ int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta);
+ assert newCodeAddressDelta > 0;
+ int codeAddressDeltaLeb128Size = Leb128Utils.unsignedLeb128Size(newCodeAddressDelta);
+
+ //if the length of the new code address delta is the same, we can use the existing buffer
+ if (codeAddressDeltaLeb128Size + 1 == debugInstructionLength) {
+ result = originalEncodedDebugInfo;
+ Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1);
+ } else {
+ //The length of the new code address delta is different, so create a new buffer with enough
+ //additional space to accomodate the new code address delta value.
+ result = new byte[originalEncodedDebugInfo.length + codeAddressDeltaLeb128Size -
+ (debugInstructionLength - 1)];
+
+ System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset);
+
+ result[startDebugOffset] = DebugOpcode.DBG_ADVANCE_PC.value;
+ Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1);
+
+ System.arraycopy(originalEncodedDebugInfo, startDebugOffset + debugInstructionLength, result,
+ startDebugOffset + codeAddressDeltaLeb128Size + 1,
+ originalEncodedDebugInfo.length - (startDebugOffset + codeAddressDeltaLeb128Size + 1));
+ }
+ }
+ }
+
+ @Override
+ public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDelta,
+ int codeAddressDelta) {
+ currentCodeAddress += codeAddressDelta;
+ if (result != null) {
+ return;
+ }
+
+ int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1);
+ assert newCodeAddress != -1;
+
+ if (newCodeAddress != currentCodeAddress) {
+ int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta);
+ assert newCodeAddressDelta > 0;
+
+ //if the new code address delta won't fit in the special opcode, we need to insert
+ //an additional DBG_ADVANCE_PC opcode
+ if (lineDelta < 2 && newCodeAddressDelta > 16 || lineDelta > 1 && newCodeAddressDelta > 15) {
+ int additionalCodeAddressDelta = newCodeAddress - currentCodeAddress;
+ int additionalCodeAddressDeltaLeb128Size = Leb128Utils.signedLeb128Size(additionalCodeAddressDelta);
+
+ //create a new buffer with enough additional space for the new opcode
+ result = new byte[originalEncodedDebugInfo.length + additionalCodeAddressDeltaLeb128Size + 1];
+
+ System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset);
+ result[startDebugOffset] = 0x01; //DBG_ADVANCE_PC
+ Leb128Utils.writeUnsignedLeb128(additionalCodeAddressDelta, result, startDebugOffset+1);
+ System.arraycopy(originalEncodedDebugInfo, startDebugOffset, result,
+ startDebugOffset+additionalCodeAddressDeltaLeb128Size+1,
+ result.length - (startDebugOffset+additionalCodeAddressDeltaLeb128Size+1));
+ } else {
+ result = originalEncodedDebugInfo;
+ result[startDebugOffset] = DebugInfoBuilder.calculateSpecialOpcode(lineDelta,
+ newCodeAddressDelta);
+ }
+ }
+ }
+ }
+
+ public static class TryItem {
+ /**
+ * The address (in 2-byte words) within the code where the try block starts
+ */
+ private int startCodeAddress;
+
+ /**
+ * The number of 2-byte words that the try block covers
+ */
+ private int tryLength;
+
+ /**
+ * The associated exception handler
+ */
+ public final EncodedCatchHandler encodedCatchHandler;
+
+ /**
+ * Construct a new <code>TryItem</code> with the given values
+ * @param startCodeAddress the code address within the code where the try block starts
+ * @param tryLength the number of code blocks that the try block covers
+ * @param encodedCatchHandler the associated exception handler
+ */
+ public TryItem(int startCodeAddress, int tryLength, EncodedCatchHandler encodedCatchHandler) {
+ this.startCodeAddress = startCodeAddress;
+ this.tryLength = tryLength;
+ this.encodedCatchHandler = encodedCatchHandler;
+ }
+
+ /**
+ * This is used internally to construct a new <code>TryItem</code> while reading in a <code>DexFile</code>
+ * @param in the Input object to read the <code>TryItem</code> from
+ * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this <code>CodeItem</code>. The
+ * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list
+ * structure.
+ */
+ private TryItem(Input in, SparseArray<EncodedCatchHandler> encodedCatchHandlers) {
+ startCodeAddress = in.readInt();
+ tryLength = in.readShort();
+
+ encodedCatchHandler = encodedCatchHandlers.get(in.readShort());
+ if (encodedCatchHandler == null) {
+ throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem");
+ }
+ }
+
+ /**
+ * Writes the <code>TryItem</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "start_addr: 0x" + Integer.toHexString(startCodeAddress));
+ out.annotate(2, "try_length: 0x" + Integer.toHexString(tryLength) + " (" + tryLength +
+ ")");
+ out.annotate(2, "handler_off: 0x" + Integer.toHexString(encodedCatchHandler.getOffsetInList()));
+ }
+
+ out.writeInt(startCodeAddress);
+ out.writeShort(tryLength);
+ out.writeShort(encodedCatchHandler.getOffsetInList());
+ }
+
+ /**
+ * @return The address (in 2-byte words) within the code where the try block starts
+ */
+ public int getStartCodeAddress() {
+ return startCodeAddress;
+ }
+
+ /**
+ * @return The number of code blocks that the try block covers
+ */
+ public int getTryLength() {
+ return tryLength;
+ }
+ }
+
+ public static class EncodedCatchHandler {
+ /**
+ * An array of the individual exception handlers
+ */
+ public final EncodedTypeAddrPair[] handlers;
+
+ /**
+ * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all
+ * handler
+ */
+ private int catchAllHandlerAddress;
+
+ private int baseOffset;
+ private int offset;
+
+ /**
+ * Constructs a new <code>EncodedCatchHandler</code> with the given values
+ * @param handlers an array of the individual exception handlers
+ * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1
+ * if there is no catch all handler
+ */
+ public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) {
+ this.handlers = handlers;
+ this.catchAllHandlerAddress = catchAllHandlerAddress;
+ }
+
+ /**
+ * This is used internally to construct a new <code>EncodedCatchHandler</code> while reading in a
+ * <code>DexFile</code>
+ * @param dexFile the <code>DexFile</code> that is being read in
+ * @param in the Input object to read the <code>EncodedCatchHandler</code> from
+ */
+ private EncodedCatchHandler(DexFile dexFile, Input in) {
+ int handlerCount = in.readSignedLeb128();
+
+ if (handlerCount < 0) {
+ handlers = new EncodedTypeAddrPair[-1 * handlerCount];
+ } else {
+ handlers = new EncodedTypeAddrPair[handlerCount];
+ }
+
+ for (int i=0; i<handlers.length; i++) {
+ try {
+ handlers[i] = new EncodedTypeAddrPair(dexFile, in);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error while reading EncodedTypeAddrPair at index " + i);
+ }
+ }
+
+ if (handlerCount <= 0) {
+ catchAllHandlerAddress = in.readUnsignedLeb128();
+ } else {
+ catchAllHandlerAddress = -1;
+ }
+ }
+
+ /**
+ * @return the "Catch All" handler address for this <code>EncodedCatchHandler</code>, or -1 if there
+ * is no "Catch All" handler
+ */
+ public int getCatchAllHandlerAddress() {
+ return catchAllHandlerAddress;
+ }
+
+ /**
+ * @return the offset of this <code>EncodedCatchHandler</code> from the beginning of the
+ * encoded_catch_handler_list structure
+ */
+ private int getOffsetInList() {
+ return offset-baseOffset;
+ }
+
+ /**
+ * Places the <code>EncodedCatchHandler</code>, storing the offset and baseOffset, and returning the offset
+ * immediately following this <code>EncodedCatchHandler</code>
+ * @param offset the offset of this <code>EncodedCatchHandler</code> in the <code>DexFile</code>
+ * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the
+ * <code>DexFile</code>
+ * @return the offset immediately following this <code>EncodedCatchHandler</code>
+ */
+ private int place(int offset, int baseOffset) {
+ this.offset = offset;
+ this.baseOffset = baseOffset;
+
+ int size = handlers.length;
+ if (catchAllHandlerAddress > -1) {
+ size *= -1;
+ offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress);
+ }
+ offset += Leb128Utils.signedLeb128Size(size);
+
+ for (EncodedTypeAddrPair handler: handlers) {
+ offset += handler.getSize();
+ }
+ return offset;
+ }
+
+ /**
+ * Writes the <code>EncodedCatchHandler</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("size: 0x" + Integer.toHexString(handlers.length) + " (" + handlers.length + ")");
+
+ int size = handlers.length;
+ if (catchAllHandlerAddress > -1) {
+ size = size * -1;
+ }
+ out.writeSignedLeb128(size);
+
+ int index = 0;
+ for (EncodedTypeAddrPair handler: handlers) {
+ out.annotate(0, "[" + index++ + "] encoded_type_addr_pair");
+ out.indent();
+ handler.writeTo(out);
+ out.deindent();
+ }
+
+ if (catchAllHandlerAddress > -1) {
+ out.annotate("catch_all_addr: 0x" + Integer.toHexString(catchAllHandlerAddress));
+ out.writeUnsignedLeb128(catchAllHandlerAddress);
+ }
+ } else {
+ int size = handlers.length;
+ if (catchAllHandlerAddress > -1) {
+ size = size * -1;
+ }
+ out.writeSignedLeb128(size);
+
+ for (EncodedTypeAddrPair handler: handlers) {
+ handler.writeTo(out);
+ }
+
+ if (catchAllHandlerAddress > -1) {
+ out.writeUnsignedLeb128(catchAllHandlerAddress);
+ }
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ for (EncodedTypeAddrPair handler: handlers) {
+ hash = hash * 31 + handler.hashCode();
+ }
+ hash = hash * 31 + catchAllHandlerAddress;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ EncodedCatchHandler other = (EncodedCatchHandler)o;
+ if (handlers.length != other.handlers.length || catchAllHandlerAddress != other.catchAllHandlerAddress) {
+ return false;
+ }
+
+ for (int i=0; i<handlers.length; i++) {
+ if (!handlers[i].equals(other.handlers[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ public static class EncodedTypeAddrPair {
+ /**
+ * The type of the <code>Exception</code> that this handler handles
+ */
+ public final TypeIdItem exceptionType;
+
+ /**
+ * The address (in 2-byte words) in the code of the handler
+ */
+ private int handlerAddress;
+
+ /**
+ * Constructs a new <code>EncodedTypeAddrPair</code> with the given values
+ * @param exceptionType the type of the <code>Exception</code> that this handler handles
+ * @param handlerAddress the address (in 2-byte words) in the code of the handler
+ */
+ public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) {
+ this.exceptionType = exceptionType;
+ this.handlerAddress = handlerAddress;
+ }
+
+ /**
+ * This is used internally to construct a new <code>EncodedTypeAddrPair</code> while reading in a
+ * <code>DexFile</code>
+ * @param dexFile the <code>DexFile</code> that is being read in
+ * @param in the Input object to read the <code>EncodedCatchHandler</code> from
+ */
+ private EncodedTypeAddrPair(DexFile dexFile, Input in) {
+ exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128());
+ handlerAddress = in.readUnsignedLeb128();
+ }
+
+ /**
+ * @return the size of this <code>EncodedTypeAddrPair</code>
+ */
+ private int getSize() {
+ return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) +
+ Leb128Utils.unsignedLeb128Size(handlerAddress);
+ }
+
+ /**
+ * Writes the <code>EncodedTypeAddrPair</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ */
+ private void writeTo(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("exception_type: " + exceptionType.getTypeDescriptor());
+ out.writeUnsignedLeb128(exceptionType.getIndex());
+
+ out.annotate("handler_addr: 0x" + Integer.toHexString(handlerAddress));
+ out.writeUnsignedLeb128(handlerAddress);
+ } else {
+ out.writeUnsignedLeb128(exceptionType.getIndex());
+ out.writeUnsignedLeb128(handlerAddress);
+ }
+ }
+
+ public int getHandlerAddress() {
+ return handlerAddress;
+ }
+
+ @Override
+ public int hashCode() {
+ return exceptionType.hashCode() * 31 + handlerAddress;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ EncodedTypeAddrPair other = (EncodedTypeAddrPair)o;
+ return exceptionType == other.exceptionType && handlerAddress == other.handlerAddress;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Convertible.java b/dexlib/src/main/java/org/jf/dexlib/Convertible.java
new file mode 100644
index 0000000..f227621
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Convertible.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+/**
+ * Describes an object that can be converted to a different type
+ */
+public interface Convertible<T> {
+ T convert();
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java
new file mode 100644
index 0000000..b61f399
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java
@@ -0,0 +1,364 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Debug;
+
+import org.jf.dexlib.DebugInfoItem;
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.ByteArrayInput;
+import org.jf.dexlib.Util.Input;
+
+public class DebugInstructionIterator {
+ /**
+ * This method decodes the debug instructions in the given byte array and iterates over them, calling
+ * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
+ * @param in an Input object that the debug instructions can be read from
+ * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
+ * for each instruction that is encountered
+ */
+ public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) {
+ int startDebugOffset;
+
+ while(true)
+ {
+ startDebugOffset = in.getCursor();
+ byte debugOpcode = in.readByte();
+
+ switch (debugOpcode) {
+ case 0x00:
+ {
+ processDebugInstruction.ProcessEndSequence(startDebugOffset);
+ return;
+ }
+ case 0x01:
+ {
+ int codeAddressDiff = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset,
+ codeAddressDiff);
+ break;
+ }
+ case 0x02:
+ {
+ int lineDiff = in.readSignedLeb128();
+ processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset,
+ lineDiff);
+ break;
+ }
+ case 0x03:
+ {
+ int registerNum = in.readUnsignedOrSignedLeb128();
+ boolean isSignedRegister = false;
+ if (registerNum < 0) {
+ isSignedRegister = true;
+ registerNum = ~registerNum;
+ }
+ int nameIndex = in.readUnsignedLeb128() - 1;
+ int typeIndex = in.readUnsignedLeb128() - 1;
+ processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset,
+ registerNum, nameIndex, typeIndex, isSignedRegister);
+ break;
+ }
+ case 0x04:
+ {
+ int registerNum = in.readUnsignedOrSignedLeb128();
+ boolean isSignedRegister = false;
+ if (registerNum < 0) {
+ isSignedRegister = true;
+ registerNum = ~registerNum;
+ }
+ int nameIndex = in.readUnsignedLeb128() - 1;
+ int typeIndex = in.readUnsignedLeb128() - 1;
+ int signatureIndex = in.readUnsignedLeb128() - 1;
+ processDebugInstruction.ProcessStartLocalExtended(startDebugOffset,
+ in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex,
+ isSignedRegister);
+ break;
+ }
+ case 0x05:
+ {
+ int registerNum = in.readUnsignedOrSignedLeb128();
+ boolean isSignedRegister = false;
+ if (registerNum < 0) {
+ isSignedRegister = true;
+ registerNum = ~registerNum;
+ }
+ processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset,
+ registerNum, isSignedRegister);
+ break;
+ }
+ case 0x06:
+ {
+ int registerNum = in.readUnsignedOrSignedLeb128();
+ boolean isSignedRegister = false;
+ if (registerNum < 0) {
+ isSignedRegister = true;
+ registerNum = ~registerNum;
+ }
+ processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset,
+ registerNum, isSignedRegister);
+ break;
+ }
+ case 0x07:
+ {
+ processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset);
+ break;
+ }
+ case 0x08:
+ {
+ processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset);
+ break;
+ }
+ case 0x09:
+ {
+ int nameIndex = in.readUnsignedLeb128();
+ processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset,
+ nameIndex);
+ break;
+ }
+ default:
+ {
+ int base = ((debugOpcode & 0xFF) - 0x0A);
+ processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method decodes the debug instructions in the given byte array and iterates over them, calling
+ * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
+ * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over
+ * @param registerCount the number of registers in the method that the given debug info is for
+ * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
+ * for each instruction that is encountered
+ */
+ public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount,
+ ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) {
+ int startDebugOffset;
+ int currentCodeAddress = 0;
+ int line = debugInfoItem.getLineStart();
+ Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo());
+ DexFile dexFile = debugInfoItem.getDexFile();
+
+ Local[] locals = new Local[registerCount];
+
+ while(true)
+ {
+ startDebugOffset = in.getCursor();
+ byte debugOpcode = in.readByte();
+
+ switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) {
+ case DBG_END_SEQUENCE:
+ {
+ return;
+ }
+ case DBG_ADVANCE_PC:
+ {
+ int codeAddressDiff = in.readUnsignedLeb128();
+ currentCodeAddress += codeAddressDiff;
+ break;
+ }
+ case DBG_ADVANCE_LINE:
+ {
+ int lineDiff = in.readSignedLeb128();
+ line += lineDiff;
+ break;
+ }
+ case DBG_START_LOCAL:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ locals[registerNum] = new Local(registerNum, name, type, null);
+ processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress,
+ in.getCursor() - startDebugOffset, registerNum, name, type);
+ break;
+ }
+ case DBG_START_LOCAL_EXTENDED:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ StringIdItem signature =
+ dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ locals[registerNum] = new Local(registerNum, name, type, signature);
+ processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress,
+ in.getCursor() - startDebugOffset, registerNum, name, type, signature);
+ break;
+ }
+ case DBG_END_LOCAL:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ Local local = locals[registerNum];
+ if (local == null) {
+ processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum,
+ null, null, null);
+ } else {
+ processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum,
+ local.name, local.type, local.signature);
+ }
+ break;
+ }
+ case DBG_RESTART_LOCAL:
+ {
+ int registerNum = in.readUnsignedLeb128();
+ Local local = locals[registerNum];
+ if (local == null) {
+ processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset,
+ registerNum, null, null, null);
+ } else {
+ processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset,
+ registerNum, local.name, local.type, local.signature);
+ }
+
+ break;
+ }
+ case DBG_SET_PROLOGUE_END:
+ {
+ processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress);
+ break;
+ }
+ case DBG_SET_EPILOGUE_BEGIN:
+ {
+ processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress);
+ break;
+ }
+ case DBG_SET_FILE:
+ {
+ StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name);
+ break;
+ }
+ case DBG_SPECIAL_OPCODE:
+ {
+ int base = ((debugOpcode & 0xFF) - 0x0A);
+ currentCodeAddress += base / 15;
+ line += (base % 15) - 4;
+ processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line);
+ }
+ }
+ }
+ }
+
+ public static class ProcessRawDebugInstructionDelegate
+ {
+ //TODO: add javadocs
+ public void ProcessEndSequence(int startDebugOffset) {
+ ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1);
+ }
+
+ public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) {
+ ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length);
+ }
+
+ public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) {
+ ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length);
+ }
+
+ public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex,
+ boolean registerIsSigned) {
+ }
+
+ public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex,int signatureIndex, boolean registerIsSigned) {
+ }
+
+ public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) {
+ ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length);
+ }
+
+ public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) {
+ ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length);
+ }
+
+ public void ProcessSetPrologueEnd(int startDebugOffset) {
+ ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1);
+ }
+
+ public void ProcessSetEpilogueBegin(int startDebugOffset) {
+ ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1);
+ }
+
+ public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
+ }
+
+ public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) {
+ ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1);
+ }
+
+ public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) {
+ }
+ }
+
+ public static class ProcessDecodedDebugInstructionDelegate
+ {
+ public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type) {
+ }
+
+ public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type, StringIdItem signature) {
+ }
+
+ public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type,
+ StringIdItem signature) {
+ }
+
+ public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
+ TypeIdItem type, StringIdItem signature) {
+ }
+
+ public void ProcessSetPrologueEnd(int codeAddress) {
+ }
+
+ public void ProcessSetEpilogueBegin(int codeAddress) {
+ }
+
+ public void ProcessSetFile(int codeAddress, int length, StringIdItem name) {
+ }
+
+ public void ProcessLineEmit(int codeAddress, int line) {
+ }
+ }
+
+ private static class Local {
+ public final int register;
+ public final StringIdItem name;
+ public final TypeIdItem type;
+ public final StringIdItem signature;
+ public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) {
+ this.register = register;
+ this.name = name;
+ this.type = type;
+ this.signature = signature;
+ }
+
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java
new file mode 100644
index 0000000..d219ee3
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java
@@ -0,0 +1,64 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Debug;
+
+public enum DebugOpcode {
+ DBG_END_SEQUENCE((byte)0x00),
+ DBG_ADVANCE_PC((byte)0x01),
+ DBG_ADVANCE_LINE((byte)0x02),
+ DBG_START_LOCAL((byte)0x03),
+ DBG_START_LOCAL_EXTENDED((byte)0x04),
+ DBG_END_LOCAL((byte)0x05),
+ DBG_RESTART_LOCAL((byte)0x06),
+ DBG_SET_PROLOGUE_END((byte)0x07),
+ DBG_SET_EPILOGUE_BEGIN((byte)0x08),
+ DBG_SET_FILE((byte)0x09),
+ DBG_SPECIAL_OPCODE((byte)0x0A);
+
+ private static DebugOpcode[] opcodesByValue;
+
+ static {
+ opcodesByValue = new DebugOpcode[11];
+
+ for (DebugOpcode debugOpcode: DebugOpcode.values()) {
+ opcodesByValue[debugOpcode.value & 0xFF] = debugOpcode;
+ }
+ }
+
+ public static DebugOpcode getDebugOpcodeByValue(byte debugOpcodeValue) {
+ debugOpcodeValue = (byte)Math.min(debugOpcodeValue & 0xFF, 0x0A);
+ return opcodesByValue[debugOpcodeValue];
+ }
+
+ public final byte value;
+
+ DebugOpcode(byte value) {
+ this.value = value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java
new file mode 100644
index 0000000..81e023d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java
@@ -0,0 +1,620 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Debug.DebugInstructionIterator;
+import org.jf.dexlib.Debug.DebugOpcode;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.ByteArrayInput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Leb128Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DebugInfoItem extends Item<DebugInfoItem> {
+ private int lineStart;
+ private StringIdItem[] parameterNames;
+ private byte[] encodedDebugInfo;
+ private Item[] referencedItems;
+
+ private CodeItem parent = null;
+
+ /**
+ * Creates a new uninitialized <code>DebugInfoInfo</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ public DebugInfoItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>DebugInfoItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param lineStart the initial value for the line number register for the debug info machine
+ * @param parameterNames an array of the names of the associated method's parameters. The entire parameter
+ * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter
+ * @param encodedDebugInfo the debug info, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded
+ * debug info
+ */
+ private DebugInfoItem(DexFile dexFile,
+ int lineStart,
+ StringIdItem[] parameterNames,
+ byte[] encodedDebugInfo,
+ Item[] referencedItems) {
+ super(dexFile);
+ this.lineStart = lineStart;
+ this.parameterNames = parameterNames;
+ this.encodedDebugInfo = encodedDebugInfo;
+ this.referencedItems = referencedItems;
+ }
+
+ /**
+ * Returns a new <code>DebugInfoItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param lineStart the initial value for the line number register for the debug info machine
+ * @param parameterNames an array of the names of the associated method's parameters. The entire parameter
+ * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter
+ * @param encodedDebugInfo the debug info, encoded as a byte array
+ * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded
+ * debug info
+ * @return a new <code>DebugInfoItem</code> with the given values
+ */
+ public static DebugInfoItem internDebugInfoItem(DexFile dexFile,
+ int lineStart,
+ StringIdItem[] parameterNames,
+ byte[] encodedDebugInfo,
+ Item[] referencedItems) {
+ DebugInfoItem debugInfoItem = new DebugInfoItem(dexFile, lineStart, parameterNames, encodedDebugInfo,
+ referencedItems);
+ return dexFile.DebugInfoItemsSection.intern(debugInfoItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ lineStart = in.readUnsignedLeb128();
+ parameterNames = new StringIdItem[in.readUnsignedLeb128()];
+ IndexedSection<StringIdItem> stringIdSection = dexFile.StringIdsSection;
+ for (int i=0; i<parameterNames.length; i++) {
+ parameterNames[i] = stringIdSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1);
+ }
+
+ int start = in.getCursor();
+ final List<Item> referencedItemsList = new ArrayList<Item>(50);
+ DebugInstructionIterator.IterateInstructions(in,
+ new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() {
+ @Override
+ public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, boolean registerIsSigned) {
+ if (nameIndex != -1) {
+ referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex));
+ }
+ if (typeIndex != -1) {
+ referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex));
+ }
+ }
+
+ @Override
+ public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNume,
+ int nameIndex, int typeIndex, int signatureIndex,
+ boolean registerIsSigned) {
+ if (nameIndex != -1) {
+ referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex));
+ }
+ if (typeIndex != -1) {
+ referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex));
+ }
+ if (signatureIndex != -1) {
+ referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(signatureIndex));
+ }
+ }
+
+ @Override
+ public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
+ if (nameIndex != -1) {
+ referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex));
+ }
+ }
+ });
+
+ referencedItems = new Item[referencedItemsList.size()];
+ referencedItemsList.toArray(referencedItems);
+
+ int length = in.getCursor() - start;
+ in.setCursor(start);
+ encodedDebugInfo = in.readBytes(length);
+ }
+
+
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ offset += Leb128Utils.unsignedLeb128Size(lineStart);
+ offset += Leb128Utils.unsignedLeb128Size(parameterNames.length);
+ for (StringIdItem parameterName: parameterNames) {
+ int indexp1;
+ if (parameterName == null) {
+ indexp1 = 0;
+ } else {
+ indexp1 = parameterName.getIndex() + 1;
+ }
+ offset += Leb128Utils.unsignedLeb128Size(indexp1);
+ }
+
+ //make a subclass so we can keep track of and access the computed length
+ class ProcessDebugInstructionDelegateWithLength extends
+ DebugInstructionIterator.ProcessRawDebugInstructionDelegate {
+ public int length = 0;
+ }
+ ProcessDebugInstructionDelegateWithLength pdidwl;
+
+ //final referencedItems = this.referencedItems;
+
+ DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo),
+ pdidwl = new ProcessDebugInstructionDelegateWithLength() {
+ private int referencedItemsPosition = 0;
+
+ @Override
+ public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) {
+ this.length+=length;
+ }
+
+ @Override
+ public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, boolean registerIsSigned) {
+ this.length++;
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ this.length += Leb128Utils.signedLeb128Size(registerNum);
+ } else {
+ this.length+=Leb128Utils.unsignedLeb128Size(registerNum);
+ }
+ if (nameIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+ if (typeIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+
+ }
+
+ @Override
+ public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, int signatureIndex,
+ boolean registerIsSigned) {
+ this.length++;
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ this.length += Leb128Utils.signedLeb128Size(registerNum);
+ } else {
+ this.length+=Leb128Utils.unsignedLeb128Size(registerNum);
+ }
+ if (nameIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+ if (typeIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+ if (signatureIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+ }
+
+ @Override
+ public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
+ this.length++;
+ if (nameIndex != -1) {
+ this.length+=
+ Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1);
+ } else {
+ this.length++;
+ }
+ }
+ });
+ return offset + pdidwl.length;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(final AnnotatedOutput out) {
+ if (out.annotates()) {
+ writeItemWithAnnotations(out);
+ } else {
+ writeItemWithNoAnnotations(out);
+ }
+ }
+
+ /**
+ * Replaces the encoded debug info for this DebugInfoItem. It is expected that the new debug info is compatible
+ * with the existing information, i.e. lineStart, referencedItems, parameterNames
+ * @param encodedDebugInfo the new encoded debug info
+ */
+ protected void setEncodedDebugInfo(byte[] encodedDebugInfo) {
+ //TODO: I would rather replace this method with some way of saying "The (code) instruction at address changed from A bytes to B bytes. Fixup the debug info accordingly"
+
+ this.encodedDebugInfo = encodedDebugInfo;
+ }
+
+ /**
+ * Helper method that writes the item, without writing annotations
+ * @param out the AnnotatedOutput object
+ */
+ private void writeItemWithNoAnnotations(final AnnotatedOutput out) {
+ out.writeUnsignedLeb128(lineStart);
+ out.writeUnsignedLeb128(parameterNames.length);
+ for (StringIdItem parameterName: parameterNames) {
+ int indexp1;
+ if (parameterName == null) {
+ indexp1 = 0;
+ } else {
+ indexp1 = parameterName.getIndex() + 1;
+ }
+ out.writeUnsignedLeb128(indexp1);
+ }
+
+ DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo),
+ new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() {
+ private int referencedItemsPosition = 0;
+
+ @Override
+ public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) {
+ out.write(encodedDebugInfo, startDebugOffset, length);
+ }
+
+ @Override
+ public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, boolean registerIsSigned) {
+ out.writeByte(DebugOpcode.DBG_START_LOCAL.value);
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ if (nameIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ if (typeIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ }
+
+ @Override
+ public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, int signatureIndex,
+ boolean registerIsSigned) {
+ out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value);
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ if (nameIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ if (typeIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ if (signatureIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ }
+
+ @Override
+ public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
+ out.writeByte(DebugOpcode.DBG_SET_FILE.value);
+ if (nameIndex != -1) {
+ out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1);
+ } else {
+ out.writeByte(0);
+ }
+ }
+ });
+ }
+
+ /**
+ * Helper method that writes and annotates the item
+ * @param out the AnnotatedOutput object
+ */
+ private void writeItemWithAnnotations(final AnnotatedOutput out) {
+ out.annotate(0, parent.getParent().method.getMethodString());
+ out.annotate("line_start: 0x" + Integer.toHexString(lineStart) + " (" + lineStart + ")");
+ out.writeUnsignedLeb128(lineStart);
+ out.annotate("parameters_size: 0x" + Integer.toHexString(parameterNames.length) + " (" + parameterNames.length
+ + ")");
+ out.writeUnsignedLeb128(parameterNames.length);
+ int index = 0;
+ for (StringIdItem parameterName: parameterNames) {
+ int indexp1;
+ if (parameterName == null) {
+ out.annotate("[" + index++ +"] parameterName: ");
+ indexp1 = 0;
+ } else {
+ out.annotate("[" + index++ +"] parameterName: " + parameterName.getStringValue());
+ indexp1 = parameterName.getIndex() + 1;
+ }
+ out.writeUnsignedLeb128(indexp1);
+ }
+
+ DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo),
+ new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() {
+ private int referencedItemsPosition = 0;
+
+ @Override
+ public void ProcessEndSequence(int startDebugOffset) {
+ out.annotate("DBG_END_SEQUENCE");
+ out.writeByte(DebugOpcode.DBG_END_SEQUENCE.value);
+ }
+
+ @Override
+ public void ProcessAdvancePC(int startDebugOffset, int length, int addressDiff) {
+ out.annotate("DBG_ADVANCE_PC");
+ out.writeByte(DebugOpcode.DBG_ADVANCE_PC.value);
+ out.indent();
+ out.annotate("addr_diff: 0x" + Integer.toHexString(addressDiff) + " (" + addressDiff + ")");
+ out.writeUnsignedLeb128(addressDiff);
+ out.deindent();
+ }
+
+ @Override
+ public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) {
+ out.annotate("DBG_ADVANCE_LINE");
+ out.writeByte(DebugOpcode.DBG_ADVANCE_LINE.value);
+ out.indent();
+ out.annotate("line_diff: 0x" + Integer.toHexString(lineDiff) + " (" + lineDiff + ")");
+ out.writeSignedLeb128(lineDiff);
+ out.deindent();
+ }
+
+ @Override
+ public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex,
+ int typeIndex, boolean registerIsSigned) {
+ out.annotate("DBG_START_LOCAL");
+ out.writeByte(DebugOpcode.DBG_START_LOCAL.value);
+ out.indent();
+ out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")");
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ if (nameIndex != -1) {
+ Item nameItem = referencedItems[referencedItemsPosition++];
+ assert nameItem instanceof StringIdItem;
+ out.annotate("name: " + ((StringIdItem)nameItem).getStringValue());
+ out.writeUnsignedLeb128(nameItem.getIndex() + 1);
+ } else {
+ out.annotate("name: ");
+ out.writeByte(0);
+ }
+ if (typeIndex != -1) {
+ Item typeItem = referencedItems[referencedItemsPosition++];
+ assert typeItem instanceof TypeIdItem;
+ out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor());
+ out.writeUnsignedLeb128(typeItem.getIndex() + 1);
+ } else {
+ out.annotate("type: ");
+ out.writeByte(0);
+ }
+ out.deindent();
+ }
+
+ @Override
+ public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum,
+ int nameIndex, int typeIndex, int signatureIndex,
+ boolean registerIsSigned) {
+ out.annotate("DBG_START_LOCAL_EXTENDED");
+ out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value);
+ out.indent();
+ out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")");
+ if (dexFile.getPreserveSignedRegisters() && registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ if (nameIndex != -1) {
+ Item nameItem = referencedItems[referencedItemsPosition++];
+ assert nameItem instanceof StringIdItem;
+ out.annotate("name: " + ((StringIdItem)nameItem).getStringValue());
+ out.writeUnsignedLeb128(nameItem.getIndex() + 1);
+ } else {
+ out.annotate("name: ");
+ out.writeByte(0);
+ }
+ if (typeIndex != -1) {
+ Item typeItem = referencedItems[referencedItemsPosition++];
+ assert typeItem instanceof TypeIdItem;
+ out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor());
+ out.writeUnsignedLeb128(typeItem.getIndex() + 1);
+ } else {
+ out.annotate("type: ");
+ out.writeByte(0);
+ }
+ if (signatureIndex != -1) {
+ Item signatureItem = referencedItems[referencedItemsPosition++];
+ assert signatureItem instanceof StringIdItem;
+ out.annotate("signature: " + ((StringIdItem)signatureItem).getStringValue());
+ out.writeUnsignedLeb128(signatureItem.getIndex() + 1);
+ } else {
+ out.annotate("signature: ");
+ out.writeByte(0);
+ }
+ out.deindent();
+ }
+
+ @Override
+ public void ProcessEndLocal(int startDebugOffset, int length, int registerNum,
+ boolean registerIsSigned) {
+ out.annotate("DBG_END_LOCAL");
+ out.writeByte(DebugOpcode.DBG_END_LOCAL.value);
+ out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")");
+ if (registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ }
+
+ @Override
+ public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum,
+ boolean registerIsSigned) {
+ out.annotate("DBG_RESTART_LOCAL");
+ out.writeByte(DebugOpcode.DBG_RESTART_LOCAL.value);
+ out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")");
+ if (registerIsSigned) {
+ out.writeSignedLeb128(registerNum);
+ } else {
+ out.writeUnsignedLeb128(registerNum);
+ }
+ }
+
+ @Override
+ public void ProcessSetPrologueEnd(int startDebugOffset) {
+ out.annotate("DBG_SET_PROLOGUE_END");
+ out.writeByte(DebugOpcode.DBG_SET_PROLOGUE_END.value);
+ }
+
+ @Override
+ public void ProcessSetEpilogueBegin(int startDebugOffset) {
+ out.annotate("DBG_SET_EPILOGUE_BEGIN");
+ out.writeByte(DebugOpcode.DBG_SET_EPILOGUE_BEGIN.value);
+ }
+
+ @Override
+ public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) {
+ out.annotate("DBG_SET_FILE");
+ out.writeByte(DebugOpcode.DBG_SET_FILE.value);
+ if (nameIndex != -1) {
+ Item sourceItem = referencedItems[referencedItemsPosition++];
+ assert sourceItem instanceof StringIdItem;
+ out.annotate("source_file: \"" + ((StringIdItem)sourceItem).getStringValue() + "\"");
+ out.writeUnsignedLeb128(sourceItem.getIndex() + 1);
+ } else {
+ out.annotate("source_file: ");
+ out.writeByte(0);
+ }
+ }
+
+ @Override
+ public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff,
+ int addressDiff) {
+ out.annotate("DBG_SPECIAL_OPCODE: line_diff=0x" + Integer.toHexString(lineDiff) + "(" +
+ lineDiff +"),addressDiff=0x" + Integer.toHexString(addressDiff) + "(" + addressDiff +
+ ")");
+ out.writeByte(debugOpcode);
+ }
+ });
+ }
+
+
+
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_DEBUG_INFO_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "debug_info_item @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(DebugInfoItem other) {
+ if (parent == null) {
+ if (other.parent == null) {
+ return 0;
+ }
+ return -1;
+ }
+ if (other.parent == null) {
+ return 1;
+ }
+ return parent.compareTo(other.parent);
+ }
+
+ /**
+ * Set the <code>CodeItem</code> that this <code>DebugInfoItem</code> is associated with
+ * @param codeItem the <code>CodeItem</code> that this <code>DebugInfoItem</code> is associated with
+ */
+ protected void setParent(CodeItem codeItem) {
+ this.parent = codeItem;
+ }
+
+ /**
+ * @return the initial value for the line number register for the debug info machine
+ */
+ public int getLineStart() {
+ return lineStart;
+ }
+
+ /**
+ * @return the debug info, encoded as a byte array
+ */
+ public byte[] getEncodedDebugInfo() {
+ return encodedDebugInfo;
+ }
+
+ /**
+ * @return an array of the items referenced by instructions, in order of occurance in the encoded debug info
+ */
+ public Item[] getReferencedItems() {
+ return referencedItems;
+ }
+
+ /**
+ * @return an array of the names of the associated method's parameters. The array can be null if no parameter info
+ * is available, or any element can be null to indicate no info for that parameter
+ */
+ public StringIdItem[] getParameterNames() {
+ return parameterNames;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/dexlib/src/main/java/org/jf/dexlib/DexFile.java
new file mode 100644
index 0000000..80f25f8
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/DexFile.java
@@ -0,0 +1,901 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.*;
+
+import java.io.*;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.zip.Adler32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * <h3>Main use cases</h3>
+ *
+ * <p>These are the main use cases that drove the design of this library</p>
+ *
+ * <ol>
+ * <li><p><b>Annotate an existing dex file</b> - In this case, the intent is to document the structure of
+ * an existing dex file. We want to be able to read in the dex file, and then write out a dex file
+ * that is exactly the same (while adding annotation information to an AnnotatedOutput object)</p></li>
+ *
+ * <li><p><b>Canonicalize an existing dex file</b> - In this case, the intent is to rewrite an existing dex file
+ * so that it is in a canonical form. There is a certain amount of leeway in how various types of
+ * tems in a dex file are ordered or represented. It is sometimes useful to be able to easily
+ * compare a disassebled and reassembled dex file with the original dex file. If both dex-files are
+ * written canonically, they "should" match exactly, barring any explicit changes to the reassembled
+ * file.</p>
+ *
+ * <p>Currently, there are a couple of pieces of information that probably won't match exactly
+ * <ul>
+ * <li>the order of exception handlers in the <code>EncodedCatchHandlerList</code> for a method</li>
+ * <li>the ordering of some of the debug info in the <code>{@link org.jf.dexlib.DebugInfoItem}</code> for a method</li>
+ * </ul></p>
+ *
+ *
+ * <p>Note that the above discrepancies should typically only be "intra-item" differences. They
+ * shouldn't change the size of the item, or affect how anything else is placed or laid out</p></li>
+ *
+ * <li><p><b>Creating a dex file from scratch</b> - In this case, a blank dex file is created and then classes
+ * are added to it incrementally by calling the {@link org.jf.dexlib.Section#intern intern} method of
+ * {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given
+ * class. For example, when assembling a dex file from a set of assembly text files.</p>
+ *
+ * <p>In this case, we can choose to write the dex file in a canonical form or not. It is somewhat
+ * slower to write it in a canonical format, due to the extra sorting and calculations that are
+ * required.</p></li>
+ *
+ *
+ * <li><p><b>Reading in the dex file</b> - In this case, the intent is to read in a dex file and expose all the
+ * data to the calling application. For example, when disassembling a dex file into a text based
+ * assembly format, or doing other misc processing of the dex file.</p></li>
+ *
+ *
+ * <h3>Other use cases</h3>
+ *
+ * <p>These are other use cases that are possible, but did not drive the design of the library.
+ * No effort was made to test these use cases or ensure that they work. Some of these could
+ * probably be better achieved with a disassemble - modify - reassemble type process, using
+ * smali/baksmali or another assembler/disassembler pair that are compatible with each other</p>
+ *
+ * <ul>
+ * <li>deleting classes/methods/etc. from a dex file</li>
+ * <li>merging 2 dex files</li>
+ * <li>splitting a dex file</li>
+ * <li>moving classes from 1 dex file to another</li>
+ * <li>removing the debug information from a dex file</li>
+ * <li>obfustication of a dex file</li>
+ * </ul>
+ */
+public class DexFile
+{
+ /**
+ * A mapping from ItemType to the section that contains items of the given type
+ */
+ private final Section[] sectionsByType;
+
+ /**
+ * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order
+ * that the sections will be written in
+ */
+ private final IndexedSection[] indexedSections;
+ private final OffsettedSection[] offsettedSections;
+
+ /**
+ * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb
+ * format, instead of an unsigned leb format. There are no negative registers of course, but
+ * certain positive values have a different encoding depending on whether they are encoded as
+ * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases.
+ *
+ * This determine whether we should keep any signed registers as signed, or force all register to
+ * unsigned. By default we don't keep track of whether they were signed or not, and write them back
+ * out as unsigned. This option only has an effect when reading an existing dex file. It has no
+ * effect when a dex file is created from scratch
+ *
+ * The 2 main use-cases in play are
+ * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep
+ * track of any signed registers and write them back out as signed Leb128 values.
+ *
+ * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all
+ * registers in the debug info are written as unsigned Leb128 values regardless of how they were
+ * originally encoded
+ */
+ private final boolean preserveSignedRegisters;
+
+ /**
+ * When true, any instructions in a code item are skipped over instead of being read in. This is useful when
+ * you only need the information about the classes and their methods, for example, when loading the BOOTCLASSPATH
+ * jars in order to analyze a dex file
+ */
+ private final boolean skipInstructions;
+
+ /**
+ * When true, this prevents any sorting of the items during placement of the dex file. This
+ * should *only* be set to true when this dex file was read in from an existing (valid) dex file,
+ * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that
+ * an invalid dex file will be generated.
+ *
+ * This is useful for the first use case (annotating an existing dex file). This ensures the items
+ * retain the same order as in the original dex file.
+ */
+ private boolean inplace = false;
+
+ /**
+ * When true, this imposes an full ordering on all the items, to force them into a (possibly
+ * arbitrary) canonical order. When false, only the items that the dex format specifies
+ * an order for are sorted. The rest of the items are not ordered.
+ *
+ * This is useful for the second use case (canonicalizing an existing dex file) or possibly for
+ * the third use case (creating a dex file from scratch), if there is a need to write the new
+ * dex file in a canonical form.
+ */
+ private boolean sortAllItems = false;
+
+ /**
+ * Is this file an odex file? This is only set when reading in an odex file
+ */
+ private boolean isOdex = false;
+
+ private OdexHeader odexHeader;
+ private OdexDependencies odexDependencies;
+
+ private int dataOffset;
+ private int dataSize;
+ private int fileSize;
+
+ /**
+ * A private constructor containing common code to initialize the section maps and lists
+ * @param preserveSignedRegisters If true, keep track of any registers in the debug information
+ * @param skipInstructions If true, skip the instructions in any code item.
+ * that are signed, so they will be written in the same format. See
+ * <code>getPreserveSignedRegisters()</code>
+ */
+ private DexFile(boolean preserveSignedRegisters, boolean skipInstructions) {
+ this.preserveSignedRegisters = preserveSignedRegisters;
+ this.skipInstructions = skipInstructions;
+
+ sectionsByType = new Section[] {
+ StringIdsSection,
+ TypeIdsSection,
+ ProtoIdsSection,
+ FieldIdsSection,
+ MethodIdsSection,
+ ClassDefsSection,
+ TypeListsSection,
+ AnnotationSetRefListsSection,
+ AnnotationSetsSection,
+ ClassDataSection,
+ CodeItemsSection,
+ AnnotationDirectoriesSection,
+ StringDataSection,
+ DebugInfoItemsSection,
+ AnnotationsSection,
+ EncodedArraysSection,
+ null,
+ null
+ };
+
+ indexedSections = new IndexedSection[] {
+ StringIdsSection,
+ TypeIdsSection,
+ ProtoIdsSection,
+ FieldIdsSection,
+ MethodIdsSection,
+ ClassDefsSection
+ };
+
+ offsettedSections = new OffsettedSection[] {
+ AnnotationSetRefListsSection,
+ AnnotationSetsSection,
+ CodeItemsSection,
+ AnnotationDirectoriesSection,
+ TypeListsSection,
+ StringDataSection,
+ AnnotationsSection,
+ EncodedArraysSection,
+ ClassDataSection,
+ DebugInfoItemsSection
+ };
+ }
+
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file.
+ * @param file The dex file to read in
+ * @throws IOException if an IOException occurs
+ */
+ public DexFile(String file)
+ throws IOException {
+ this(new File(file), true, false);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file,
+ * and optionally keep track of any registers in the debug information that are signed,
+ * so they will be written in the same format.
+ * @param file The dex file to read in
+ * @param preserveSignedRegisters If true, keep track of any registers in the debug information
+ * that are signed, so they will be written in the same format. See
+ * @param skipInstructions If true, skip the instructions in any code item.
+ * <code>getPreserveSignedRegisters()</code>
+ * @throws IOException if an IOException occurs
+ */
+ public DexFile(String file, boolean preserveSignedRegisters, boolean skipInstructions)
+ throws IOException {
+ this(new File(file), preserveSignedRegisters, skipInstructions);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file.
+ * @param file The dex file to read in
+ * @throws IOException if an IOException occurs
+ */
+ public DexFile(File file)
+ throws IOException {
+ this(file, true, false);
+ }
+
+ /**
+ * Construct a new DexFile instance by reading in the given dex file,
+ * and optionally keep track of any registers in the debug information that are signed,
+ * so they will be written in the same format.
+ * @param file The dex file to read in
+ * @param preserveSignedRegisters If true, keep track of any registers in the debug information
+ * that are signed, so they will be written in the same format.
+ * @param skipInstructions If true, skip the instructions in any code item.
+ * @see #getPreserveSignedRegisters
+ * @throws IOException if an IOException occurs
+ */
+ public DexFile(File file, boolean preserveSignedRegisters, boolean skipInstructions)
+ throws IOException {
+ this(preserveSignedRegisters, skipInstructions);
+
+ long fileLength;
+ byte[] magic = FileUtils.readFile(file, 0, 8);
+
+ InputStream inputStream = null;
+ Input in = null;
+ ZipFile zipFile = null;
+
+ try {
+ //do we have a zip file?
+ if (magic[0] == 0x50 && magic[1] == 0x4B) {
+ zipFile = new ZipFile(file);
+ ZipEntry zipEntry = zipFile.getEntry("classes.dex");
+ if (zipEntry == null) {
+ throw new NoClassesDexException("zip file " + file.getName() + " does not contain a classes.dex " +
+ "file");
+ }
+ fileLength = zipEntry.getSize();
+ if (fileLength < 40) {
+ throw new RuntimeException("The classes.dex file in " + file.getName() + " is too small to be a" +
+ " valid dex file");
+ } else if (fileLength > Integer.MAX_VALUE) {
+ throw new RuntimeException("The classes.dex file in " + file.getName() + " is too large to read in");
+ }
+ inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry));
+
+ inputStream.mark(8);
+ for (int i=0; i<8; i++) {
+ magic[i] = (byte)inputStream.read();
+ }
+ inputStream.reset();
+ } else {
+ fileLength = file.length();
+ if (fileLength < 40) {
+ throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
+ }
+ if (fileLength < 40) {
+ throw new RuntimeException(file.getName() + " is too small to be a valid dex file");
+ } else if (fileLength > Integer.MAX_VALUE) {
+ throw new RuntimeException(file.getName() + " is too large to read in");
+ }
+ inputStream = new FileInputStream(file);
+ }
+
+ byte[] dexMagic, odexMagic;
+ boolean isDex = false;
+ this.isOdex = false;
+
+ for (int i=0; i<HeaderItem.MAGIC_VALUES.length; i++) {
+ byte[] magic_value = HeaderItem.MAGIC_VALUES[i];
+ if (Arrays.equals(magic, magic_value)) {
+ isDex = true;
+ break;
+ }
+ }
+ if (!isDex) {
+ if (Arrays.equals(magic, OdexHeader.MAGIC_35)) {
+ isOdex = true;
+ } else if (Arrays.equals(magic, OdexHeader.MAGIC_36)) {
+ isOdex = true;
+ }
+ }
+
+ if (isOdex) {
+ byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
+ Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes);
+ odexHeader = new OdexHeader(odexHeaderIn);
+
+ int dependencySkip = odexHeader.depsOffset - odexHeader.dexOffset - odexHeader.dexLength;
+ if (dependencySkip < 0) {
+ throw new ExceptionWithContext("Unexpected placement of the odex dependency data");
+ }
+
+ if (odexHeader.dexOffset > 40) {
+ FileUtils.readStream(inputStream, odexHeader.dexOffset - 40);
+ }
+
+ in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength));
+
+ if (dependencySkip > 0) {
+ FileUtils.readStream(inputStream, dependencySkip);
+ }
+
+ odexDependencies = new OdexDependencies(
+ new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.depsLength)));
+ } else if (isDex) {
+ in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
+ } else {
+ StringBuffer sb = new StringBuffer("bad magic value:");
+ for (int i=0; i<8; i++) {
+ sb.append(" ");
+ sb.append(Hex.u1(magic[i]));
+ }
+ throw new RuntimeException(sb.toString());
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ }
+
+ ReadContext readContext = new ReadContext();
+
+ HeaderItem.readFrom(in, 0, readContext);
+
+ //the map offset was set while reading in the header item
+ int mapOffset = readContext.getSectionOffset(ItemType.TYPE_MAP_LIST);
+
+ in.setCursor(mapOffset);
+ MapItem.readFrom(in, 0, readContext);
+
+ //the sections are ordered in such a way that the item types
+ Section sections[] = new Section[] {
+ StringDataSection,
+ StringIdsSection,
+ TypeIdsSection,
+ TypeListsSection,
+ ProtoIdsSection,
+ FieldIdsSection,
+ MethodIdsSection,
+ AnnotationsSection,
+ AnnotationSetsSection,
+ AnnotationSetRefListsSection,
+ AnnotationDirectoriesSection,
+ DebugInfoItemsSection,
+ CodeItemsSection,
+ ClassDataSection,
+ EncodedArraysSection,
+ ClassDefsSection
+ };
+
+ for (Section section: sections) {
+ if (section == null) {
+ continue;
+ }
+
+ if (skipInstructions && (section == CodeItemsSection || section == DebugInfoItemsSection)) {
+ continue;
+ }
+
+ int sectionOffset = readContext.getSectionOffset(section.ItemType);
+ if (sectionOffset > 0) {
+ int sectionSize = readContext.getSectionSize(section.ItemType);
+ in.setCursor(sectionOffset);
+ section.readFrom(sectionSize, in, readContext);
+ }
+ }
+ }
+
+ /**
+ * Constructs a new, blank dex file. Classes can be added to this dex file by calling
+ * the <code>Section.intern()</code> method of <code>ClassDefsSection</code>
+ */
+ public DexFile() {
+ this(true, false);
+ }
+
+ /**
+ * Get the <code>Section</code> containing items of the same type as the given item
+ * @param item Get the <code>Section</code> that contains items of this type
+ * @param <T> The specific item subclass - inferred from the passed item
+ * @return the <code>Section</code> containing items of the same type as the given item
+ */
+ public <T extends Item> Section<T> getSectionForItem(T item) {
+ return (Section<T>)sectionsByType[item.getItemType().SectionIndex];
+ }
+
+ /**
+ * Get the <code>Section</code> containing items of the given type
+ * @param itemType the type of item
+ * @return the <code>Section</code> containing items of the given type
+ */
+ public Section getSectionForType(ItemType itemType) {
+ return sectionsByType[itemType.SectionIndex];
+ }
+
+ /**
+ * Get a boolean value indicating whether this dex file preserved any signed
+ * registers in the debug info as it read the dex file in. By default, the dex file
+ * doesn't check whether the registers are encoded as unsigned or signed values.
+ *
+ * This does *not* affect the actual register value that is read in. The value is
+ * read correctly regardless
+ *
+ * This does affect whether any signed registers will retain the same encoding or be
+ * forced to the (correct) unsigned encoding when the dex file is written back out.
+ *
+ * See the discussion about signed register values in the documentation for
+ * <code>DexFile</code>
+ * @return a boolean indicating whether this dex file preserved any signed registers
+ * as it was read in
+ */
+ public boolean getPreserveSignedRegisters() {
+ return preserveSignedRegisters;
+ }
+
+ /**
+ * Get a boolean value indicating whether to skip any instructions in a code item while reading in the dex file.
+ * This is useful when you only need the information about the classes and their methods, for example, when
+ * loading the BOOTCLASSPATH jars in order to analyze a dex file
+ * @return a boolean value indicating whether to skip any instructions in a code item
+ */
+ public boolean skipInstructions() {
+ return skipInstructions;
+ }
+
+ /**
+ * Get a boolean value indicating whether all items should be placed into a
+ * (possibly arbitrary) "canonical" ordering. If false, then only the items
+ * that must be ordered per the dex specification are sorted.
+ *
+ * When true, writing the dex file involves somewhat more overhead
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @return a boolean value indicating whether all items should be sorted
+ */
+ public boolean getSortAllItems() {
+ return this.sortAllItems;
+ }
+
+ /**
+ * Set a boolean value indicating whether all items should be placed into a
+ * (possibly arbitrary) "canonical" ordering. If false, then only the items
+ * that must be ordered per the dex specification are sorted.
+ *
+ * When true, writing the dex file involves somewhat more overhead
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @param value a boolean value indicating whether all items should be sorted
+ */
+ public void setSortAllItems(boolean value) {
+ this.sortAllItems = value;
+ }
+
+ /**
+ * @return a boolean value indicating whether this dex file was created by reading in an odex file
+ */
+ public boolean isOdex() {
+ return this.isOdex;
+ }
+
+ /**
+ * @return an OdexDependencies object that contains the dependencies for this odex, or null if this
+ * DexFile represents a dex file instead of an odex file
+ */
+ public OdexDependencies getOdexDependencies() {
+ return odexDependencies;
+ }
+
+ /**
+ * @return An OdexHeader object containing the information from the odex header in this dex file, or null if there
+ * is no odex header
+ */
+ public OdexHeader getOdexHeader() {
+ return odexHeader;
+ }
+
+ /**
+ * Get a boolean value indicating whether items in this dex file should be
+ * written back out "in-place", or whether the normal layout logic should be
+ * applied.
+ *
+ * This should only be used for a dex file that has been read from an existing
+ * dex file, and no modifications have been made to the dex file. Otherwise,
+ * there is a good chance that the resulting dex file will be invalid due to
+ * items that aren't placed correctly
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @return a boolean value indicating whether items in this dex file should be
+ * written back out in-place.
+ */
+ public boolean getInplace() {
+ return this.inplace;
+ }
+
+ /**
+ * @return the size of the file, in bytes
+ */
+ public int getFileSize() {
+ return fileSize;
+ }
+
+ /**
+ * @return the size of the data section, in bytes
+ */
+ public int getDataSize() {
+ return dataSize;
+ }
+
+ /**
+ * @return the offset where the data section begins
+ */
+ public int getDataOffset() {
+ return dataOffset;
+ }
+
+ /**
+ * Set a boolean value indicating whether items in this dex file should be
+ * written back out "in-place", or whether the normal layout logic should be
+ * applied.
+ *
+ * This should only be used for a dex file that has been read from an existing
+ * dex file, and no modifications have been made to the dex file. Otherwise,
+ * there is a good chance that the resulting dex file will be invalid due to
+ * items that aren't placed correctly
+ *
+ * If both SortAllItems and Inplace are true, Inplace takes precedence
+ * @param value a boolean value indicating whether items in this dex file should be
+ * written back out in-place.
+ */
+ public void setInplace(boolean value) {
+ this.inplace = value;
+ }
+
+ /**
+ * Get an array of Section objects that are sorted by offset.
+ * @return an array of Section objects that are sorted by offset.
+ */
+ protected Section[] getOrderedSections() {
+ int sectionCount = 0;
+
+ for (Section section: sectionsByType) {
+ if (section != null && section.getItems().size() > 0) {
+ sectionCount++;
+ }
+ }
+
+ Section[] sections = new Section[sectionCount];
+ sectionCount = 0;
+ for (Section section: sectionsByType) {
+ if (section != null && section.getItems().size() > 0) {
+ sections[sectionCount++] = section;
+ }
+ }
+
+ Arrays.sort(sections, new Comparator<Section>() {
+ public int compare(Section a, Section b) {
+ return a.getOffset() - b.getOffset();
+ }
+ });
+
+ return sections;
+ }
+
+ /**
+ * This method should be called before writing a dex file. It sorts the sections
+ * as needed or as indicated by <code>getSortAllItems()</code> and <code>getInplace()</code>,
+ * and then performs a pass through all of the items, finalizing the position (i.e.
+ * index and/or offset) of each item in the dex file.
+ *
+ * This step is needed primarily so that the indexes and offsets of all indexed and
+ * offsetted items are available when writing references to those items elsewhere.
+ */
+ public void place() {
+ int offset = HeaderItem.placeAt(0, 0);
+
+ int sectionsPosition = 0;
+ Section[] sections;
+ if (this.inplace) {
+ sections = this.getOrderedSections();
+ } else {
+ sections = new Section[indexedSections.length + offsettedSections.length];
+ System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length);
+ System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length);
+ }
+
+ while (sectionsPosition < sections.length && sections[sectionsPosition].ItemType.isIndexedItem()) {
+ Section section = sections[sectionsPosition];
+ if (!this.inplace) {
+ section.sortSection();
+ }
+
+ offset = section.placeAt(offset);
+
+ sectionsPosition++;
+ }
+
+ dataOffset = offset;
+
+ while (sectionsPosition < sections.length) {
+ Section section = sections[sectionsPosition];
+ if (this.sortAllItems && !this.inplace) {
+ section.sortSection();
+ }
+ offset = section.placeAt(offset);
+
+ sectionsPosition++;
+ }
+
+ offset = AlignmentUtils.alignOffset(offset, ItemType.TYPE_MAP_LIST.ItemAlignment);
+ offset = MapItem.placeAt(offset, 0);
+
+ fileSize = offset;
+ dataSize = offset - dataOffset;
+ }
+
+ /**
+ * Writes the dex file to the give <code>AnnotatedOutput</code> object. If
+ * <code>out.Annotates()</code> is true, then annotations that document the format
+ * of the dex file are written.
+ *
+ * You must call <code>place()</code> on this dex file, before calling this method
+ * @param out the AnnotatedOutput object to write the dex file and annotations to
+ *
+ * After calling this method, you should call <code>calcSignature()</code> and
+ * then <code>calcChecksum()</code> on the resulting byte array, to calculate the
+ * signature and checksum in the header
+ */
+ public void writeTo(AnnotatedOutput out) {
+
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, "header item");
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, " ");
+ HeaderItem.writeTo(out);
+
+ out.annotate(0, " ");
+
+ int sectionsPosition = 0;
+ Section[] sections;
+ if (this.inplace) {
+ sections = this.getOrderedSections();
+ } else {
+ sections = new Section[indexedSections.length + offsettedSections.length];
+ System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length);
+ System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length);
+ }
+
+ while (sectionsPosition < sections.length) {
+ sections[sectionsPosition].writeTo(out);
+ sectionsPosition++;
+ }
+
+ out.alignTo(MapItem.getItemType().ItemAlignment);
+
+ out.annotate(0, " ");
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, "map item");
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, " ");
+ MapItem.writeTo(out);
+ }
+
+ public final HeaderItem HeaderItem = new HeaderItem(this);
+ public final MapItem MapItem = new MapItem(this);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>StringIdItem</code> items
+ */
+ public final IndexedSection<StringIdItem> StringIdsSection =
+ new IndexedSection<StringIdItem>(this, ItemType.TYPE_STRING_ID_ITEM);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>TypeIdItem</code> items
+ */
+ public final IndexedSection<TypeIdItem> TypeIdsSection =
+ new IndexedSection<TypeIdItem>(this, ItemType.TYPE_TYPE_ID_ITEM);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>ProtoIdItem</code> items
+ */
+ public final IndexedSection<ProtoIdItem> ProtoIdsSection =
+ new IndexedSection<ProtoIdItem>(this, ItemType.TYPE_PROTO_ID_ITEM);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>FieldIdItem</code> items
+ */
+ public final IndexedSection<FieldIdItem> FieldIdsSection =
+ new IndexedSection<FieldIdItem>(this, ItemType.TYPE_FIELD_ID_ITEM);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>MethodIdItem</code> items
+ */
+ public final IndexedSection<MethodIdItem> MethodIdsSection =
+ new IndexedSection<MethodIdItem>(this, ItemType.TYPE_METHOD_ID_ITEM);
+
+ /**
+ * The <code>IndexedSection</code> containing <code>ClassDefItem</code> items
+ */
+ public final IndexedSection<ClassDefItem> ClassDefsSection =
+ new IndexedSection<ClassDefItem>(this, ItemType.TYPE_CLASS_DEF_ITEM) {
+
+ public int placeAt(int offset) {
+ if (DexFile.this.getInplace()) {
+ return super.placeAt(offset);
+ }
+
+ int ret = ClassDefItem.placeClassDefItems(this, offset);
+
+ Collections.sort(this.items);
+
+ this.offset = items.get(0).getOffset();
+ return ret;
+ }
+
+ protected void sortSection() {
+ // Do nothing. Sorting is handled by ClassDefItem.ClassDefPlacer, during placement
+ }
+ };
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>TypeListItem</code> items
+ */
+ public final OffsettedSection<TypeListItem> TypeListsSection =
+ new OffsettedSection<TypeListItem>(this, ItemType.TYPE_TYPE_LIST);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>AnnotationSetRefList</code> items
+ */
+ public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection =
+ new OffsettedSection<AnnotationSetRefList>(this, ItemType.TYPE_ANNOTATION_SET_REF_LIST);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>AnnotationSetItem</code> items
+ */
+ public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection =
+ new OffsettedSection<AnnotationSetItem>(this, ItemType.TYPE_ANNOTATION_SET_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>ClassDataItem</code> items
+ */
+ public final OffsettedSection<ClassDataItem> ClassDataSection =
+ new OffsettedSection<ClassDataItem>(this, ItemType.TYPE_CLASS_DATA_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>CodeItem</code> items
+ */
+ public final OffsettedSection<CodeItem> CodeItemsSection =
+ new OffsettedSection<CodeItem>(this, ItemType.TYPE_CODE_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>StringDataItem</code> items
+ */
+ public final OffsettedSection<StringDataItem> StringDataSection =
+ new OffsettedSection<StringDataItem>(this, ItemType.TYPE_STRING_DATA_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>DebugInfoItem</code> items
+ */
+ public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection =
+ new OffsettedSection<DebugInfoItem>(this, ItemType.TYPE_DEBUG_INFO_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>AnnotationItem</code> items
+ */
+ public final OffsettedSection<AnnotationItem> AnnotationsSection =
+ new OffsettedSection<AnnotationItem>(this, ItemType.TYPE_ANNOTATION_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>EncodedArrayItem</code> items
+ */
+ public final OffsettedSection<EncodedArrayItem> EncodedArraysSection =
+ new OffsettedSection<EncodedArrayItem>(this, ItemType.TYPE_ENCODED_ARRAY_ITEM);
+
+ /**
+ * The <code>OffsettedSection</code> containing <code>AnnotationDirectoryItem</code> items
+ */
+ public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection =
+ new OffsettedSection<AnnotationDirectoryItem>(this, ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM);
+
+
+ /**
+ * Calculates the signature for the dex file in the given byte array,
+ * and then writes the signature to the appropriate location in the header
+ * containing in the array
+ *
+ * @param bytes non-null; the bytes of the file
+ */
+ public static void calcSignature(byte[] bytes) {
+ MessageDigest md;
+
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ md.update(bytes, 32, bytes.length - 32);
+
+ try {
+ int amt = md.digest(bytes, 12, 20);
+ if (amt != 20) {
+ throw new RuntimeException("unexpected digest write: " + amt +
+ " bytes");
+ }
+ } catch (DigestException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Calculates the checksum for the <code>.dex</code> file in the
+ * given array, and modify the array to contain it.
+ *
+ * @param bytes non-null; the bytes of the file
+ */
+ public static void calcChecksum(byte[] bytes) {
+ Adler32 a32 = new Adler32();
+
+ a32.update(bytes, 12, bytes.length - 12);
+
+ int sum = (int) a32.getValue();
+
+ bytes[8] = (byte) sum;
+ bytes[9] = (byte) (sum >> 8);
+ bytes[10] = (byte) (sum >> 16);
+ bytes[11] = (byte) (sum >> 24);
+ }
+
+ public static class NoClassesDexException extends ExceptionWithContext {
+ public NoClassesDexException(String message) {
+ super(message);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java b/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java
new file mode 100644
index 0000000..6eb917c
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java
@@ -0,0 +1,135 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class EncodedArrayItem extends Item<EncodedArrayItem> {
+ private int hashCode = 0;
+
+ private ArrayEncodedSubValue encodedArray;
+
+ /**
+ * Creates a new uninitialized <code>EncodedArrayItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected EncodedArrayItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>EncodedArrayItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param encodedArray The encoded array value
+ */
+ private EncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) {
+ super(dexFile);
+ this.encodedArray = encodedArray;
+ }
+
+ /**
+ * Returns an <code>EncodedArrayItem</code> for the given values, and that has been interned into the given
+ * <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param encodedArray The encoded array value
+ * @return an <code>EncodedArrayItem</code> for the given values, and that has been interned into the given
+ */
+ public static EncodedArrayItem internEncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) {
+ EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, encodedArray);
+ return dexFile.EncodedArraysSection.intern(encodedArrayItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ encodedArray = new ArrayEncodedSubValue(dexFile, in);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return encodedArray.placeValue(offset);
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ encodedArray.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_ENCODED_ARRAY_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "encoded_array @0x" + Integer.toHexString(getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(EncodedArrayItem encodedArrayItem) {
+ return encodedArray.compareTo(encodedArrayItem.encodedArray);
+ }
+
+ /**
+ * @return The encoded array value
+ */
+ public ArrayEncodedSubValue getEncodedArray() {
+ return encodedArray;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = encodedArray.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ EncodedArrayItem other = (EncodedArrayItem)o;
+ return (encodedArray.compareTo(other.encodedArray) == 0);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java
new file mode 100644
index 0000000..acd5996
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java
@@ -0,0 +1,169 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Leb128Utils;
+
+/**
+ * An <code>AnnotationEncodedSubValue</code> is identical to an <code>AnnotationEncodedValue</code>, except that it
+ * doesn't have the initial valueType/valueArg byte. This is used in the <code>AnnotationItem</code> object
+ */
+public class AnnotationEncodedSubValue extends EncodedValue {
+ private int hashCode = 0;
+
+ public final TypeIdItem annotationType;
+ public final StringIdItem[] names;
+ public final EncodedValue[] values;
+
+ /**
+ * Constructs a new <code>AnnotationEncodedSubValue</code> by reading the value from the given <code>Input</code>
+ * object.
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ */
+ public AnnotationEncodedSubValue(DexFile dexFile, Input in) {
+ annotationType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128());
+ names = new StringIdItem[in.readUnsignedLeb128()];
+ values = new EncodedValue[names.length];
+
+ for (int i=0; i<names.length; i++) {
+ names[i] = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128());
+ values[i] = EncodedValue.readEncodedValue(dexFile, in);
+ }
+ }
+
+ /**
+ * Constructs a new <code>AnnotationEncodedValue</code> with the given values. names and values must be the same
+ * length, and must be sorted according to the name
+ * @param annotationType The type of the annotation
+ * @param names An array of the names of the elements of the annotation
+ * @param values An array of the values of the elements on the annotation
+ */
+ public AnnotationEncodedSubValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) {
+ this.annotationType = annotationType;
+ if (names.length != values.length) {
+ throw new RuntimeException("The names and values parameters must be the same length");
+ }
+ this.names = names;
+ this.values = values;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ out.annotate("annotation_type: " + annotationType.getTypeDescriptor());
+ out.writeUnsignedLeb128(annotationType.getIndex());
+ out.annotate("element_count: 0x" + Integer.toHexString(names.length) + " (" + names.length + ")");
+ out.writeUnsignedLeb128(names.length);
+
+ for (int i=0; i<names.length; i++) {
+ out.annotate(0, "[" + i + "] annotation_element");
+ out.indent();
+ out.annotate("element_name: " + names[i].getStringValue());
+ out.writeUnsignedLeb128(names[i].getIndex());
+ out.annotate(0, "element_value:");
+ out.indent();
+ values[i].writeValue(out);
+ out.deindent();
+ out.deindent();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ offset = offset + Leb128Utils.unsignedLeb128Size(annotationType.getIndex());
+ offset = offset + Leb128Utils.unsignedLeb128Size(names.length);
+
+ for (int i=0; i<names.length; i++) {
+ offset = offset + Leb128Utils.unsignedLeb128Size(names[i].getIndex());
+ offset = values[i].placeValue(offset);
+ }
+
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ AnnotationEncodedSubValue other = (AnnotationEncodedSubValue)o;
+
+ int comp = annotationType.compareTo(other.annotationType);
+ if (comp != 0) {
+ return comp;
+ }
+
+ comp = names.length - other.names.length;
+ if (comp != 0) {
+ return comp;
+ }
+
+ for (int i=0; i<names.length; i++) {
+ comp = names[i].compareTo(other.names[i]);
+ if (comp != 0) {
+ return comp;
+ }
+
+ comp = values[i].compareTo(other.values[i]);
+ if (comp != 0) {
+ return comp;
+ }
+ }
+
+ return comp;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_ANNOTATION;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = annotationType.hashCode();
+
+ for (int i=0; i<names.length; i++) {
+ hashCode = 31 * hashCode + names[i].hashCode();
+ hashCode = 31 * hashCode + values[i].hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedValue.java
new file mode 100644
index 0000000..4e9c4e1
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedValue.java
@@ -0,0 +1,72 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class AnnotationEncodedValue extends AnnotationEncodedSubValue {
+ /**
+ * Constructs a new <code>AnnotationEncodedValue</code> by reading the value from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ */
+ protected AnnotationEncodedValue(DexFile dexFile, Input in) {
+ super(dexFile, in);
+ }
+
+ /**
+ * Constructs a new <code>AnnotationEncodedValue</code> with the given values. names and values must be the same
+ * length, and must be sorted according to the name
+ * @param annotationType The type of the annotation
+ * @param names An array of the names of the elements of the annotation
+ * @param values An array of the values of the elements on the annotation
+ */
+ public AnnotationEncodedValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) {
+ super(annotationType, names, values);
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("value_type=" + ValueType.VALUE_ANNOTATION.name() + ",value_arg=0");
+ }
+ out.writeByte(ValueType.VALUE_ANNOTATION.value);
+ super.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return super.placeValue(offset + 1);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java
new file mode 100644
index 0000000..f79db73
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java
@@ -0,0 +1,141 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Leb128Utils;
+
+/**
+ * An <code>ArrayEncodedSubValue</code> is identical to an <code>ArrayEncodedValue</code>, except that it
+ * doesn't have the initial valueType/valueArg byte. This is used in the <code>EncodedArrayItem</code> object
+ */
+public class ArrayEncodedSubValue extends EncodedValue {
+ private int hashCode = 0;
+
+ public final EncodedValue[] values;
+
+ /**
+ * Constructs a new <code>ArrayEncodedSubValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ */
+ public ArrayEncodedSubValue(DexFile dexFile, Input in) {
+ values = new EncodedValue[in.readUnsignedLeb128()];
+
+ for (int i=0; i<values.length; i++) {
+ values[i] = EncodedValue.readEncodedValue(dexFile, in);
+ }
+ }
+
+ /**
+ * Constructs a new <code>ArrayEncodedSubValue</code> with the given values
+ * @param values The array values
+ */
+ public ArrayEncodedSubValue(EncodedValue[] values) {
+ this.values = values;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates())
+ {
+ out.annotate("array_size: 0x" + Integer.toHexString(values.length) + " (" + values.length + ")");
+ out.writeUnsignedLeb128(values.length);
+ int index = 0;
+ for (EncodedValue encodedValue: values) {
+ out.annotate(0, "[" + index++ + "] array_element");
+ out.indent();
+ encodedValue.writeValue(out);
+ out.deindent();
+ }
+ } else {
+ out.writeUnsignedLeb128(values.length);
+ for (EncodedValue encodedValue: values) {
+ encodedValue.writeValue(out);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ offset = offset + Leb128Utils.unsignedLeb128Size(values.length);
+ for (EncodedValue encodedValue: values) {
+ offset = encodedValue.placeValue(offset);
+ }
+
+ return offset;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ ArrayEncodedSubValue other = (ArrayEncodedSubValue)o;
+
+ int comp = values.length - other.values.length;
+ if (comp != 0) {
+ return comp;
+ }
+
+ for (int i=0; i<values.length; i++) {
+ comp = values[i].compareTo(other.values[i]);
+ if (comp != 0) {
+ return comp;
+ }
+ }
+
+ return comp;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_ARRAY;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = 0;
+
+ for (EncodedValue encodedValue: values) {
+ hashCode = 31 * hashCode + encodedValue.hashCode();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedValue.java
new file mode 100644
index 0000000..c9a3fc9
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedValue.java
@@ -0,0 +1,67 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class ArrayEncodedValue extends ArrayEncodedSubValue {
+ /**
+ * Constructs a new <code>ArrayEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ */
+ protected ArrayEncodedValue(DexFile dexFile, Input in) {
+ super(dexFile, in);
+ }
+
+ /**
+ * Constructs a new <code>ArrayEncodedValue</code> with the given values
+ * @param values The array values
+ */
+ public ArrayEncodedValue(EncodedValue[] values) {
+ super(values);
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("value_type=" + ValueType.VALUE_ARRAY.name() + ",value_arg=0");
+ }
+ out.writeByte(ValueType.VALUE_ARRAY.value);
+ super.writeValue(out);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return super.placeValue(offset + 1);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java
new file mode 100644
index 0000000..507e58f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java
@@ -0,0 +1,109 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class BooleanEncodedValue extends EncodedValue {
+ /**
+ * The dupliton values
+ */
+ public static final BooleanEncodedValue TrueValue = new BooleanEncodedValue(true);
+ public static final BooleanEncodedValue FalseValue = new BooleanEncodedValue(false);
+
+ public final boolean value;
+
+ /**
+ * Constructs a new <code>BooleanEncodedValue</code> with the given value
+ * @param value The value
+ */
+ private BooleanEncodedValue(boolean value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the <code>BooleanEncodedValue</code> for the given valueArg value. The high 3 bits of the first byte should
+ * be passed as the valueArg parameter
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ * @return the <code>BooleanEncodedValue</code> for the given valueArg value
+ */
+ protected static BooleanEncodedValue getBooleanEncodedValue(byte valueArg) {
+ if (valueArg == 0) {
+ return FalseValue;
+ } else if (valueArg == 1) {
+ return TrueValue;
+ }
+ throw new RuntimeException("valueArg must be either 0 or 1");
+ }
+
+ /**
+ * Gets the <code>BooleanEncodedValue</code> for the given boolean value
+ * @param value the boolean value
+ * @return the <code>BooleanEncodedValue</code> for the given boolean value
+ */
+ public static BooleanEncodedValue getBooleanEncodedValue(boolean value) {
+ if (value) {
+ return TrueValue;
+ }
+ return FalseValue;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("value_type=" + ValueType.VALUE_BOOLEAN.name() + ",value=" + Boolean.toString(value));
+ }
+ out.writeByte(ValueType.VALUE_BOOLEAN.value | ((value?1:0) << 5));
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ BooleanEncodedValue other = (BooleanEncodedValue)o;
+ if (value == other.value)
+ return 0;
+ if (value)
+ return 1;
+ return -1;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_BOOLEAN;
+ }
+
+ @Override
+ public int hashCode() {
+ return value?1:0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java
new file mode 100644
index 0000000..683d547
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java
@@ -0,0 +1,86 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class ByteEncodedValue extends EncodedValue {
+ public final byte value;
+
+ /**
+ * Constructs a new <code>ByteEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value
+ * @param in The <code>Input</code> object to read from
+ */
+ protected ByteEncodedValue(Input in) {
+ value = (byte)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(1));
+ }
+
+ /**
+ * Constructs a new <code>ByteEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public ByteEncodedValue(byte value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_BYTE.name() + ",value_arg=0");
+ out.annotate(1, "value: 0x" + Integer.toHexString(value) + " (" + value + ")");
+ }
+ out.writeByte(ValueType.VALUE_BYTE.value);
+ out.writeByte(value);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 2;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ ByteEncodedValue other = (ByteEncodedValue)o;
+
+ return (value<other.value?-1:(value>other.value?1:0));
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_BYTE;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java
new file mode 100644
index 0000000..2564661
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java
@@ -0,0 +1,93 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class CharEncodedValue extends EncodedValue {
+ public final char value;
+
+ /**
+ * Constructs a new <code>CharEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected CharEncodedValue(Input in, byte valueArg) {
+ value = (char)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new <code>CharEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public CharEncodedValue(char value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value);
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_CHAR.name() + ",value_arg=" + (bytes.length - 1));
+ char[] c = Character.toChars(value);
+ assert c.length > 0;
+ out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " '" + c[0] + "'");
+ }
+
+ out.writeByte(ValueType.VALUE_CHAR.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ CharEncodedValue other = (CharEncodedValue)o;
+
+ return (value<other.value?-1:(value>other.value?1:0));
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_CHAR;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java
new file mode 100644
index 0000000..6b8bcc3
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java
@@ -0,0 +1,94 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class DoubleEncodedValue extends EncodedValue {
+ public final double value;
+
+ /**
+ * Constructs a new <code>DoubleEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected DoubleEncodedValue(Input in, byte valueArg) {
+ long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1));
+ value = Double.longBitsToDouble(longValue);
+ }
+
+ /**
+ * Constructs a new <code>DoubleEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public DoubleEncodedValue(double value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(Double.doubleToRawLongBits(value));
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_DOUBLE.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value);
+ }
+
+ out.writeByte(ValueType.VALUE_DOUBLE.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue(
+ Double.doubleToRawLongBits(value));
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ DoubleEncodedValue other = (DoubleEncodedValue)o;
+
+ return Double.compare(value, other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_DOUBLE;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)Double.doubleToRawLongBits(value);
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java
new file mode 100644
index 0000000..4c00faf
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java
@@ -0,0 +1,125 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public abstract class EncodedValue implements Comparable<EncodedValue> {
+ /**
+ * Writes this <code>EncodedValue</code> to the given <code>AnnotatedOutput</code> object
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ */
+ public abstract void writeValue(AnnotatedOutput out);
+
+ /**
+ * Calculates the size of this encoded value and returns offset + size;
+ * @param offset The offset to place this encoded value
+ * @return the offset immediately after this encoded value
+ */
+ public abstract int placeValue(int offset);
+
+
+ public static EncodedValue readEncodedValue(DexFile dexFile, Input in) {
+ Byte b = in.readByte();
+ ValueType valueType = ValueType.fromByte((byte)(b & 0x1f));
+ byte valueArg = (byte)((b & 0xFF) >> 5);
+
+ switch (valueType) {
+ case VALUE_BYTE:
+ return new ByteEncodedValue(in);
+ case VALUE_SHORT:
+ return new ShortEncodedValue(in, valueArg);
+ case VALUE_CHAR:
+ return new CharEncodedValue(in, valueArg);
+ case VALUE_INT:
+ return new IntEncodedValue(in, valueArg);
+ case VALUE_LONG:
+ return new LongEncodedValue(in, valueArg);
+ case VALUE_FLOAT:
+ return new FloatEncodedValue(in, valueArg);
+ case VALUE_DOUBLE:
+ return new DoubleEncodedValue(in, valueArg);
+ case VALUE_STRING:
+ return new StringEncodedValue(dexFile, in, valueArg);
+ case VALUE_TYPE:
+ return new TypeEncodedValue(dexFile, in, valueArg);
+ case VALUE_FIELD:
+ return new FieldEncodedValue(dexFile, in, valueArg);
+ case VALUE_METHOD:
+ return new MethodEncodedValue(dexFile, in, valueArg);
+ case VALUE_ENUM:
+ return new EnumEncodedValue(dexFile, in, valueArg);
+ case VALUE_ARRAY:
+ return new ArrayEncodedValue(dexFile, in);
+ case VALUE_ANNOTATION:
+ return new AnnotationEncodedValue(dexFile, in);
+ case VALUE_NULL:
+ return NullEncodedValue.NullValue;
+ case VALUE_BOOLEAN:
+ return BooleanEncodedValue.getBooleanEncodedValue(valueArg);
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(EncodedValue o) {
+ int comp = getValueType().compareTo(o.getValueType());
+ if (comp == 0) {
+ comp = compareValue(o);
+ }
+ return comp;
+ }
+
+ /**
+ * Compare the value of this <code>EncodedValue</code> against the value of the given <EncodedValue>, which
+ * is guaranteed to be of the same type as this <code>EncodedValue</code>
+ * @param o The <code>EncodedValue</code> to compare against
+ * @return A standard comparison integer value
+ */
+ protected abstract int compareValue(EncodedValue o);
+
+ /**
+ * @return the <code>ValueType</code> representing the type of this <code>EncodedValue</code>
+ */
+ public abstract ValueType getValueType();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !(o instanceof EncodedValue)) {
+ return false;
+ }
+
+ return this.compareTo((EncodedValue)o) == 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java
new file mode 100644
index 0000000..7cd1f45
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java
@@ -0,0 +1,95 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.FieldIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class EnumEncodedValue extends EncodedValue {
+ public final FieldIdItem value;
+
+ /**
+ * Constructs a new <code>EnumEncodedValue</code> by reading the field index from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected EnumEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.FieldIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new <code>EnumEncodedValue</code> with the given <code>FieldIdItem</code> value
+ * @param value The <code>FieldIdItem</code> value
+ */
+ public EnumEncodedValue(FieldIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_ENUM.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value.getFieldString());
+ }
+
+ out.writeByte(ValueType.VALUE_ENUM.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ EnumEncodedValue other = (EnumEncodedValue)o;
+
+ return value.compareTo(other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_ENUM;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java
new file mode 100644
index 0000000..6aafc62
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java
@@ -0,0 +1,95 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.FieldIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class FieldEncodedValue extends EncodedValue {
+ public final FieldIdItem value;
+
+ /**
+ * Constructs a new <code>FieldEncodedValue</code> by reading the field index from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected FieldEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.FieldIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new <code>FieldEncodedValue</code> with the given <code>FieldIdItem</code> value
+ * @param value The <code>FieldIdItem</code> value
+ */
+ public FieldEncodedValue(FieldIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_FIELD.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value.getFieldString());
+ }
+
+ out.writeByte(ValueType.VALUE_FIELD.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ FieldEncodedValue other = (FieldEncodedValue)o;
+
+ return value.compareTo(other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_FIELD;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java
new file mode 100644
index 0000000..af514f4
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java
@@ -0,0 +1,93 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class FloatEncodedValue extends EncodedValue {
+ public final float value;
+
+ /**
+ * Constructs a new <code>FloatEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected FloatEncodedValue(Input in, byte valueArg) {
+ long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1));
+ value = Float.intBitsToFloat((int)((longValue >> 32) & 0xFFFFFFFFL));
+ }
+
+ /**
+ * Constructs a new <code>FloatEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public FloatEncodedValue(float value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(((long)Float.floatToRawIntBits(value)) << 32);
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_FLOAT.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value);
+ }
+
+ out.writeByte(ValueType.VALUE_FLOAT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue(
+ ((long)Float.floatToRawIntBits(value)) << 32);
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ FloatEncodedValue other = (FloatEncodedValue)o;
+
+ return Float.compare(value, other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_FLOAT;
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToRawIntBits(value);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java
new file mode 100644
index 0000000..7da5b02
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java
@@ -0,0 +1,91 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class IntEncodedValue extends EncodedValue {
+ public final int value;
+
+ /**
+ * Constructs a new <code>IntEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected IntEncodedValue(Input in, byte valueArg) {
+ value = (int)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new <code>IntEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public IntEncodedValue(int value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_INT.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")");
+ }
+
+ out.writeByte(ValueType.VALUE_INT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ IntEncodedValue other = (IntEncodedValue)o;
+
+ return (value<other.value?-1:(value>other.value?1:0));
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_INT;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java
new file mode 100644
index 0000000..7db6ed1
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java
@@ -0,0 +1,91 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class LongEncodedValue extends EncodedValue {
+ public final long value;
+
+ /**
+ * Constructs a new <code>LongEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected LongEncodedValue(Input in, byte valueArg) {
+ value = EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new <code>LongEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public LongEncodedValue(long value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_LONG.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: 0x" + Long.toHexString(value) + " (" + value + ")");
+ }
+
+ out.writeByte(ValueType.VALUE_LONG.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ LongEncodedValue other = (LongEncodedValue)o;
+
+ return (value<other.value?-1:(value>other.value?1:0));
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_LONG;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int)value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java
new file mode 100644
index 0000000..e23450a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java
@@ -0,0 +1,95 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.MethodIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class MethodEncodedValue extends EncodedValue {
+ public final MethodIdItem value;
+
+ /**
+ * Constructs a new <code>MethodEncodedValue</code> by reading the method index from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected MethodEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.MethodIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new <code>MethodEncodedValue</code> with the given <code>MethodIdItem</code> value
+ * @param value The <code>MethodIdItem</code> value
+ */
+ public MethodEncodedValue(MethodIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_METHOD.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value.getMethodString());
+ }
+
+ out.writeByte(ValueType.VALUE_METHOD.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ MethodEncodedValue other = (MethodEncodedValue)o;
+
+ return value.compareTo(other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_METHOD;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java
new file mode 100644
index 0000000..334b82a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java
@@ -0,0 +1,72 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+
+public class NullEncodedValue extends EncodedValue {
+ /**
+ * The singleton value
+ */
+ public static final NullEncodedValue NullValue = new NullEncodedValue();
+
+ /**
+ * Constructs a new <code>NullEncodedValue</code>
+ */
+ private NullEncodedValue() {
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate("value_type=" + ValueType.VALUE_NULL.name() + ",value_arg=0");
+ }
+ out.writeByte(ValueType.VALUE_NULL.value);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_NULL;
+ }
+
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java
new file mode 100644
index 0000000..66d80e1
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java
@@ -0,0 +1,91 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class ShortEncodedValue extends EncodedValue {
+ public final short value;
+
+ /**
+ * Constructs a new <code>ShortEncodedValue</code> by reading the value from the given <code>Input</code> object.
+ * The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of
+ * the first byte should be passed as the valueArg parameter
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected ShortEncodedValue(Input in, byte valueArg) {
+ value = (short) EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1));
+ }
+
+ /**
+ * Constructs a new <code>ShortEncodedValue</code> with the given value
+ * @param value The value
+ */
+ public ShortEncodedValue(short value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value);
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_SHORT.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")");
+ }
+
+ out.writeByte(ValueType.VALUE_SHORT.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ ShortEncodedValue other = (ShortEncodedValue)o;
+
+ return (value<other.value?-1:(value>other.value?1:0));
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_SHORT;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java
new file mode 100644
index 0000000..8b32394
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java
@@ -0,0 +1,96 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.StringIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Utf8Utils;
+
+public class StringEncodedValue extends EncodedValue {
+ public final StringIdItem value;
+
+ /**
+ * Constructs a new <code>StringEncodedValue</code> by reading the string index from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected StringEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.StringIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new <code>StringEncodedValue</code> with the given <code>StringIdItem</code> value
+ * @param value The <code>StringIdItem</code> value
+ */
+ public StringEncodedValue(StringIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_STRING.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: \"" + Utf8Utils.escapeString(value.getStringValue()) + "\"");
+ }
+
+ out.writeByte(ValueType.VALUE_STRING.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ StringEncodedValue other = (StringEncodedValue)o;
+
+ return value.compareTo(other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_STRING;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java
new file mode 100644
index 0000000..335aab6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java
@@ -0,0 +1,95 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.DexFile;
+import org.jf.dexlib.TypeIdItem;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.EncodedValueUtils;
+import org.jf.dexlib.Util.Input;
+
+public class TypeEncodedValue extends EncodedValue {
+ public final TypeIdItem value;
+
+ /**
+ * Constructs a new <code>TypeEncodedValue</code> by reading the type index from the given <code>Input</code>
+ * object. The <code>Input</code>'s cursor should be set to the 2nd byte of the encoded value, and the high 3 bits
+ * of the first byte should be passed as the valueArg parameter
+ * @param dexFile The <code>DexFile</code> that is being read in
+ * @param in The <code>Input</code> object to read from
+ * @param valueArg The high 3 bits of the first byte of this encoded value
+ */
+ protected TypeEncodedValue(DexFile dexFile, Input in, byte valueArg) {
+ int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1));
+ value = dexFile.TypeIdsSection.getItemByIndex(index);
+ }
+
+ /**
+ * Constructs a new <code>TypeEncodedValue</code> with the given <code>TypeIdItem</code> value
+ * @param value The <code>TypeIdItem</code> value
+ */
+ public TypeEncodedValue(TypeIdItem value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void writeValue(AnnotatedOutput out) {
+ byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex());
+
+ if (out.annotates()) {
+ out.annotate(1, "value_type=" + ValueType.VALUE_TYPE.name() + ",value_arg=" + (bytes.length - 1));
+ out.annotate(bytes.length, "value: " + value.getTypeDescriptor());
+ }
+
+ out.writeByte(ValueType.VALUE_TYPE.value | ((bytes.length - 1) << 5));
+ out.write(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int placeValue(int offset) {
+ return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected int compareValue(EncodedValue o) {
+ TypeEncodedValue other = (TypeEncodedValue)o;
+
+ return value.compareTo(other.value);
+ }
+
+ /** {@inheritDoc} */
+ public ValueType getValueType() {
+ return ValueType.VALUE_TYPE;
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java
new file mode 100644
index 0000000..d33b0ac
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java
@@ -0,0 +1,86 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.EncodedValue;
+
+import org.jf.dexlib.Util.SparseArray;
+
+public enum ValueType {
+
+ VALUE_BYTE((byte) 0x00),
+ VALUE_SHORT((byte) 0x02),
+ VALUE_CHAR((byte) 0x03),
+ VALUE_INT((byte) 0x04),
+ VALUE_LONG((byte) 0x06),
+ VALUE_FLOAT((byte) 0x10),
+ VALUE_DOUBLE((byte) 0x11),
+ VALUE_STRING((byte) 0x17),
+ VALUE_TYPE((byte) 0x18),
+ VALUE_FIELD((byte) 0x19),
+ VALUE_METHOD((byte) 0x1a),
+ VALUE_ENUM((byte) 0x1b),
+ VALUE_ARRAY((byte) 0x1c),
+ VALUE_ANNOTATION((byte) 0x1d),
+ VALUE_NULL((byte) 0x1e),
+ VALUE_BOOLEAN((byte) 0x1f);
+
+ /**
+ * A map to facilitate looking up a <code>ValueType</code> by byte value
+ */
+ private final static SparseArray<ValueType> valueTypeIntegerMap;
+
+ static {
+ /** build the <code>valueTypeIntegerMap</code> object */
+ valueTypeIntegerMap = new SparseArray<ValueType>(16);
+
+ for (ValueType valueType : ValueType.values()) {
+ valueTypeIntegerMap.put(valueType.value, valueType);
+ }
+ }
+
+ /**
+ * The byte value for this ValueType
+ */
+ public final byte value;
+
+ private ValueType(byte value) {
+ this.value = value;
+ }
+
+ /**
+ * Converts a byte value to the corresponding ValueType enum value,
+ * or null if the value isn't a valid ValueType value
+ *
+ * @param valueType the byte value to convert to a ValueType
+ * @return the ValueType enum value corresponding to valueType, or null
+ * if not a valid ValueType value
+ */
+ public static ValueType fromByte(byte valueType) {
+ return valueTypeIntegerMap.get(valueType);
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java b/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java
new file mode 100644
index 0000000..c9fe05f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java
@@ -0,0 +1,263 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class FieldIdItem extends Item<FieldIdItem> implements Convertible<FieldIdItem> {
+ private int hashCode = 0;
+
+ private TypeIdItem classType;
+ private TypeIdItem fieldType;
+ private StringIdItem fieldName;
+
+ /**
+ * Creates a new uninitialized <code>FieldIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected FieldIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>FieldIdItem</code> for the given class, type and name
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the field is a member of
+ * @param fieldType the type of the field
+ * @param fieldName the name of the field
+ */
+ private FieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, StringIdItem fieldName) {
+ this(dexFile);
+
+ assert classType.dexFile == dexFile;
+ assert fieldType.dexFile == dexFile;
+ assert fieldName.dexFile == dexFile;
+
+ this.classType = classType;
+ this.fieldType = fieldType;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ * Returns a <code>FieldIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the field is a member of
+ * @param fieldType the type of the field
+ * @param fieldName the name of the field
+ * @return a <code>FieldIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static FieldIdItem internFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType,
+ StringIdItem fieldName) {
+ FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName);
+ return dexFile.FieldIdsSection.intern(fieldIdItem);
+ }
+
+ /**
+ * Looks up a <code>FieldIdItem</code> from the given <code>DexFile</code> for the given
+ * values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the field is a member of
+ * @param fieldType the type of the field
+ * @param fieldName the name of the field
+ * @return a <code>FieldIdItem</code> from the given <code>DexFile</code> for the given
+ * values, or null if it doesn't exist
+ */
+ public static FieldIdItem lookupFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType,
+ StringIdItem fieldName) {
+ FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName);
+ return dexFile.FieldIdsSection.getInternedItem(fieldIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ fieldType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ fieldName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 8;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(2, "class_type: " + classType.getTypeDescriptor());
+ out.annotate(2, "field_type: " + fieldType.getTypeDescriptor());
+ out.annotate(4, "field_name: " + fieldName.getStringValue());
+ }
+
+ int classIndex = classType.getIndex();
+ if (classIndex > 0xffff) {
+ throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of " +
+ "defining class %s is too large", getFieldString(), classType.getTypeDescriptor()));
+ }
+ out.writeShort(classIndex);
+
+ int typeIndex = fieldType.getIndex();
+ if (typeIndex > 0xffff) {
+ throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of field " +
+ "type %s is too large", getFieldString(), fieldType.getTypeDescriptor()));
+ }
+ out.writeShort(typeIndex);
+
+ out.writeInt(fieldName.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_FIELD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return getFieldString();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(FieldIdItem o) {
+ int result = classType.compareTo(o.classType);
+ if (result != 0) {
+ return result;
+ }
+
+ result = fieldName.compareTo(o.fieldName);
+ if (result != 0) {
+ return result;
+ }
+
+ return fieldType.compareTo(o.fieldType);
+ }
+
+ /**
+ * @return the class that this field is a member of
+ */
+ public TypeIdItem getContainingClass() {
+ return classType;
+ }
+
+ /**
+ * @return the type of this field
+ */
+ public TypeIdItem getFieldType() {
+ return fieldType;
+ }
+
+ /**
+ * @return the field name
+ */
+ public StringIdItem getFieldName() {
+ return fieldName;
+ }
+
+ String cachedFieldString = null;
+ /**
+ * @return a string formatted like LclassName;->fieldName:fieldType
+ */
+ public String getFieldString() {
+ if (cachedFieldString == null) {
+ String typeDescriptor = classType.getTypeDescriptor();
+ String fieldName = this.fieldName.getStringValue();
+ String fieldType = this.fieldType.getTypeDescriptor();
+
+ StringBuffer sb = new StringBuffer(typeDescriptor.length() + fieldName.length() + fieldType.length() + 3);
+ sb.append(typeDescriptor);
+ sb.append("->");
+ sb.append(fieldName);
+ sb.append(":");
+ sb.append(fieldType);
+ cachedFieldString = sb.toString();
+ }
+ return cachedFieldString;
+ }
+
+ String cachedShortFieldString = null;
+ /**
+ * @return a "short" string containing just the field name and type, formatted like fieldName:fieldType
+ */
+ public String getShortFieldString() {
+ if (cachedShortFieldString == null) {
+ String fieldName = this.fieldName.getStringValue();
+ String fieldType = this.fieldType.getTypeDescriptor();
+
+ StringBuffer sb = new StringBuffer(fieldName.length() + fieldType.length() + 1);
+ sb.append(fieldName);
+ sb.append(":");
+ sb.append(fieldType);
+ cachedShortFieldString = sb.toString();
+ }
+ return cachedShortFieldString;
+ }
+
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = classType.hashCode();
+ hashCode = 31 * hashCode + fieldType.hashCode();
+ hashCode = 31 * hashCode + fieldName.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ FieldIdItem other = (FieldIdItem)o;
+ return (classType == other.classType &&
+ fieldType == other.fieldType &&
+ fieldName == other.fieldName);
+ }
+
+ public FieldIdItem convert() {
+ return this;
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
new file mode 100644
index 0000000..e3f9a0f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java
@@ -0,0 +1,301 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import com.google.common.base.Preconditions;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Utf8Utils;
+
+public class HeaderItem extends Item<HeaderItem> {
+ /**
+ * the file format magic number, represented as the
+ * low-order bytes of a string
+ */
+ public static final byte[][] MAGIC_VALUES = new byte[][] {
+ new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00}, //"dex\n035" + '\0';
+ new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x36, 0x00}}; //"dex\n036" + '\0';
+
+
+ /** size of this section, in bytes */
+ private static final int HEADER_SIZE = 0x70;
+
+ /** the endianness constants */
+ private static final int LITTLE_ENDIAN = 0x12345678;
+ private static final int BIG_ENDIAN = 0x78563412;
+
+ /* Which magic value to use when writing out the header item */
+ private int magic_index = 0;
+
+ private boolean checksumSignatureSet = false;
+ private int checksum;
+ private byte[] signature;
+
+ /**
+ * Create a new uninitialized <code>HeaderItem</code>
+ * @param dexFile The <code>DexFile</code> containing this <code>HeaderItem</code>
+ */
+ protected HeaderItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ byte[] readMagic = in.readBytes(8);
+
+ boolean success = false;
+ for (int i=0; i<MAGIC_VALUES.length; i++) {
+ byte[] magic_value = MAGIC_VALUES[i];
+ boolean matched = true;
+ for (int j=0; j<8; j++) {
+ if (magic_value[j] != readMagic[j]) {
+ matched = false;
+ break;
+ }
+ }
+ if (matched) {
+ success = true;
+ magic_index = i;
+ break;
+ }
+ }
+
+ if (!success) {
+ throw new RuntimeException("Unrecognized dex magic value");
+ }
+
+ checksum = in.readInt(); //checksum
+ signature = in.readBytes(20); //signature
+ checksumSignatureSet = true;
+
+ in.readInt(); //filesize
+ in.readInt(); //header size
+
+ int endianTag = in.readInt();
+ if (endianTag == BIG_ENDIAN) {
+ throw new RuntimeException("This dex file is big endian. Only little endian is currently supported.");
+ } else if (endianTag != LITTLE_ENDIAN) {
+ throw new RuntimeException("The endian tag is not 0x12345678 or 0x78563412");
+ }
+
+ //link_size + link_off
+ if ((in.readInt() | in.readInt()) != 0) {
+ System.err.println("This dex file has a link section, which is not supported. Ignoring.");
+ }
+
+ int sectionSize;
+ int sectionOffset;
+
+ //map_offset
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_MAP_LIST, 1, sectionOffset);
+
+ //string_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_STRING_ID_ITEM, sectionSize, sectionOffset);
+
+ //type_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_TYPE_ID_ITEM, sectionSize, sectionOffset);
+
+ //proto_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_PROTO_ID_ITEM, sectionSize, sectionOffset);
+
+ //field_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_FIELD_ID_ITEM, sectionSize, sectionOffset);
+
+ //method_id_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_METHOD_ID_ITEM, sectionSize, sectionOffset);
+
+ //class_data_item
+ sectionSize = in.readInt();
+ sectionOffset = in.readInt();
+ readContext.addSection(ItemType.TYPE_CLASS_DEF_ITEM, sectionSize, sectionOffset);
+
+ in.readInt(); //data_size
+ in.readInt(); //data_off
+ }
+
+ /**
+ * Sets the dex version number.
+ *
+ * 35 is the default.
+ * 36 is for dex files that use extended opcodes (only works with ICS+)
+ *
+ * @param version - must be either 35 or 36
+ */
+ public void setVersion(int version) {
+ if (version == 35) {
+ magic_index = 0;
+ return;
+ }
+ if (version == 36) {
+ magic_index = 1;
+ return;
+ }
+ throw new RuntimeException("Invalid dex version number passed to setVersion");
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return HEADER_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ StringBuilder magicBuilder = new StringBuilder();
+ for (int i=0; i<8; i++) {
+ magicBuilder.append((char)MAGIC_VALUES[magic_index][i]);
+ }
+
+ out.annotate("magic: " + Utf8Utils.escapeString(magicBuilder.toString()));
+ out.write(MAGIC_VALUES[magic_index]);
+
+ out.annotate("checksum");
+ out.writeInt(0);
+
+ out.annotate("signature");
+ out.write(new byte[20]);
+
+ out.annotate("file_size: 0x" + Integer.toHexString(dexFile.getFileSize()) + " (" + dexFile.getFileSize() +
+ " bytes)");
+ out.writeInt(dexFile.getFileSize());
+
+ out.annotate("header_size: 0x" + Integer.toHexString(HEADER_SIZE));
+ out.writeInt(HEADER_SIZE);
+
+ out.annotate("endian_tag: 0x" + Integer.toHexString(LITTLE_ENDIAN));
+ out.writeInt(LITTLE_ENDIAN);
+
+ out.annotate("link_size: 0");
+ out.writeInt(0);
+
+ out.annotate("link_off: 0");
+ out.writeInt(0);
+
+ out.annotate("map_off: 0x" + Integer.toHexString(dexFile.MapItem.getOffset()));
+ out.writeInt(dexFile.MapItem.getOffset());
+
+ out.annotate("string_ids_size: " + dexFile.StringIdsSection.getItems().size());
+ out.writeInt(dexFile.StringIdsSection.getItems().size());
+
+ out.annotate("string_ids_off: 0x" + Integer.toHexString(dexFile.StringIdsSection.getOffset()));
+ out.writeInt(dexFile.StringIdsSection.getOffset());
+
+ out.annotate("type_ids_size: " + dexFile.TypeIdsSection.getItems().size());
+ out.writeInt(dexFile.TypeIdsSection.getItems().size());
+
+ out.annotate("type_ids_off: 0x" + Integer.toHexString(dexFile.TypeIdsSection.getOffset()));
+ out.writeInt(dexFile.TypeIdsSection.getOffset());
+
+ out.annotate("proto_ids_size: " + dexFile.ProtoIdsSection.getItems().size());
+ out.writeInt(dexFile.ProtoIdsSection.getItems().size());
+
+ out.annotate("proto_ids_off: 0x" + Integer.toHexString(dexFile.ProtoIdsSection.getOffset()));
+ out.writeInt(dexFile.ProtoIdsSection.getOffset());
+
+ out.annotate("field_ids_size: " + dexFile.FieldIdsSection.getItems().size());
+ out.writeInt(dexFile.FieldIdsSection.getItems().size());
+
+ out.annotate("field_ids_off: 0x" + Integer.toHexString(dexFile.FieldIdsSection.getOffset()));
+ out.writeInt(dexFile.FieldIdsSection.getOffset());
+
+ out.annotate("method_ids_size: " + dexFile.MethodIdsSection.getItems().size());
+ out.writeInt(dexFile.MethodIdsSection.getItems().size());
+
+ out.annotate("method_ids_off: 0x" + Integer.toHexString(dexFile.MethodIdsSection.getOffset()));
+ out.writeInt(dexFile.MethodIdsSection.getOffset());
+
+ out.annotate("class_defs_size: " + dexFile.ClassDefsSection.getItems().size());
+ out.writeInt(dexFile.ClassDefsSection.getItems().size());
+
+ out.annotate("class_defs_off: 0x" + Integer.toHexString(dexFile.ClassDefsSection.getOffset()));
+ out.writeInt(dexFile.ClassDefsSection.getOffset());
+
+ out.annotate("data_size: 0x" + Integer.toHexString(dexFile.getDataSize()) + " (" + dexFile.getDataSize() +
+ " bytes)");
+ out.writeInt(dexFile.getDataSize());
+
+ out.annotate("data_off: 0x" + Integer.toHexString(dexFile.getDataOffset()));
+ out.writeInt(dexFile.getDataOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_HEADER_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "header_item";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(HeaderItem o) {
+ //there is only 1 header item
+ return 0;
+ }
+
+ /**
+ * Get the checksum that was originally stored as part of this header item
+ *
+ * Note that this should only be called if this HeaderItem is from a DexFile that was read from disk, as opposed
+ * to one that is created from scratch.
+ *
+ * @return The addler32 checksum (as an integer) of the dex file
+ */
+ public int getChecksum() {
+ Preconditions.checkState(checksumSignatureSet,
+ "This can only be called on a DexFile that was read from disk.");
+ return checksum;
+ }
+
+ /**
+ * Get the signature that was originally stored as part of this header item
+ *
+ * Note that this should only be called if this HeaderItem is from a DexFile that was read from disk, as opposed
+ * to one that is created from scratch.
+ *
+ * @return The sha1 checksum of the dex file, as a 20-element byte array
+ */
+ public byte[] getSignature() {
+ Preconditions.checkState(checksumSignatureSet,
+ "This can only be called on a DexFile that was read from disk.");
+ return signature;
+ }
+
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java b/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java
new file mode 100644
index 0000000..3bc8056
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/IndexedSection.java
@@ -0,0 +1,81 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.Input;
+
+public class IndexedSection<T extends Item> extends Section<T> {
+
+ /**
+ * Create a new indexed section
+ * @param dexFile The <code>DexFile</code> that this section belongs to
+ * @param itemType The itemType that this section will hold
+ */
+ public IndexedSection(DexFile dexFile, ItemType itemType) {
+ super(dexFile, itemType);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItems(Input in, ReadContext readContext) {
+ for (int i = 0; i < items.size(); i++) {
+ T item = (T)ItemFactory.makeItem(ItemType, DexFile);
+ items.set(i, item);
+ item.readFrom(in, i, readContext);
+ }
+ }
+
+ /**
+ * Gets the item at the specified index in this section, or null if the index is -1
+ * @param index the index of the item to get
+ * @return the item at the specified index in this section, or null if the index is -1
+ */
+ public T getOptionalItemByIndex(int index) {
+ if (index == -1) {
+ return null;
+ }
+
+ return getItemByIndex(index);
+ }
+
+ /**
+ * Gets the item at the specified index in this section
+ * @param index the index of the item to get
+ * @return the item at the specified index in this section
+ */
+ public T getItemByIndex(int index) {
+ try {
+ //if index is out of bounds, just let it throw an exception
+ return items.get(index);
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex, "Error occured while retrieving the " + this.ItemType.TypeName +
+ " item at index " + index);
+ }
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Item.java b/dexlib/src/main/java/org/jf/dexlib/Item.java
new file mode 100644
index 0000000..98c2338
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Item.java
@@ -0,0 +1,224 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import com.google.common.base.Preconditions;
+import org.jf.dexlib.Util.AlignmentUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.Input;
+
+public abstract class Item<T extends Item> implements Comparable<T> {
+ /**
+ * The offset of this item in the dex file, or -1 if not known
+ */
+ protected int offset = -1;
+
+ /**
+ * The index of this item in the containing section, or -1 if not known
+ */
+ protected int index = -1;
+
+ /**
+ * The DexFile that this item is associatedr with
+ */
+ protected final DexFile dexFile;
+
+ /**
+ * The constructor that is used when reading in a <code>DexFile</code>
+ * @param dexFile the <code>DexFile</code> that this item is associated with
+ */
+ protected Item(DexFile dexFile) {
+ assert dexFile != null;
+
+ this.dexFile = dexFile;
+ }
+
+ /**
+ * Read in the item from the given input stream, and initialize the index
+ * @param in the <code>Input</code> object to read from
+ * @param index the index within the containing section of the item being read in
+ * @param readContext a <code>ReadContext</code> object to hold information that is
+ * only needed while reading in a file
+ */
+ protected void readFrom(Input in, int index, ReadContext readContext) {
+ try {
+ assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment);
+
+ this.offset = in.getCursor();
+ this.index = index;
+
+ this.readItem(in, readContext);
+ } catch (Exception ex) {
+ throw addExceptionContext(ex);
+ }
+ }
+
+ /**
+ * Place the item at the given offset and index, and return the offset of the byte following this item
+ * @param offset The offset to place the item at
+ * @param index The index of the item within the containing section
+ * @return The offset of the byte following this item
+ */
+ protected int placeAt(int offset, int index) {
+ try {
+ assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
+ assert !dexFile.getInplace() || (offset == this.offset && this.index == index);
+
+ this.offset = offset;
+ this.index = index;
+ return this.placeItem(offset);
+ } catch (Exception ex) {
+ throw addExceptionContext(ex);
+ }
+ }
+
+ /**
+ * Write and annotate this item to the output stream
+ * @param out The output stream to write and annotate to
+ */
+ protected void writeTo(AnnotatedOutput out) {
+ try {
+ assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
+ //ensure that it is being written to the same offset where it was previously placed
+ assert out.getCursor() == offset;
+
+ if (out.annotates()) {
+ out.annotate(0, "[" + index + "] " + this.getItemType().TypeName);
+ }
+
+ out.indent();
+ writeItem(out);
+ out.deindent();
+ } catch (Exception ex) {
+ throw addExceptionContext(ex);
+ }
+ }
+
+ /**
+ * Returns a human readable form of this item
+ * @return a human readable form of this item
+ */
+ public String toString() {
+ return getConciseIdentity();
+ }
+
+ /**
+ * The method in the concrete item subclass that actually reads in the data for the item
+ *
+ * The logic in this method can assume that the given Input object is valid and is
+ * aligned as neccessary.
+ *
+ * This method is for internal use only
+ * @param in the <code>Input</code> object to read from
+ * @param readContext a <code>ReadContext</code> object to hold information that is
+ * only needed while reading in a file
+ */
+ protected abstract void readItem(Input in, ReadContext readContext);
+
+ /**
+ * The method should finalize the layout of the item and return the offset of the byte
+ * immediately following the item.
+ *
+ * The implementation of this method can assume that the offset argument has already been
+ * aligned based on the item's alignment requirements
+ *
+ * This method is for internal use only
+ * @param offset the (pre-aligned) offset to place the item at
+ * @return the size of the item, in bytes
+ */
+ protected abstract int placeItem(int offset);
+
+ /**
+ * The method in the concrete item subclass that actually writes and annotates the data
+ * for the item.
+ *
+ * The logic in this method can assume that the given Output object is valid and is
+ * aligned as neccessary
+ *
+ * @param out The <code>AnnotatedOutput</code> object to write/annotate to
+ */
+ protected abstract void writeItem(AnnotatedOutput out);
+
+ /**
+ * This method is called to add item specific context information to an exception, to identify the "current item"
+ * when the exception occured. It adds the value returned by <code>getConciseIdentity</code> as context for the
+ * exception
+ * @param ex The exception that occured
+ * @return A RuntimeException with additional details about the item added
+ */
+ protected final RuntimeException addExceptionContext(Exception ex) {
+ return ExceptionWithContext.withContext(ex, getConciseIdentity());
+ }
+
+ /**
+ * @return An ItemType enum that represents the item type of this item
+ */
+ public abstract ItemType getItemType();
+
+ /**
+ * @return A concise (human-readable) string value that conveys the identity of this item
+ */
+ public abstract String getConciseIdentity();
+
+
+ /**
+ * Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>)
+ * @return the offset in the dex file where this item is located
+ */
+ public int getOffset() {
+ Preconditions.checkState(offset != -1,
+ "The offset is not set until the DexFile containing this item is placed.");
+ return offset;
+ }
+
+ /**
+ * Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>)
+ * @return the index of this item within the item's containing section.
+ */
+ public int getIndex() {
+ Preconditions.checkState(index != -1,
+ "The index is not set until the DexFile containing this item is placed.");
+ return index;
+ }
+
+ /**
+ * @return True if this item has been placed, otherwise False
+ */
+ public boolean isPlaced() {
+ return offset != -1;
+ }
+
+ /**
+ * @return the <code>DexFile</code> that contains this item
+ */
+ public DexFile getDexFile() {
+ return dexFile;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java b/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java
new file mode 100644
index 0000000..553d189
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java
@@ -0,0 +1,71 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+class ItemFactory {
+ protected static Item makeItem(ItemType itemType, DexFile dexFile) {
+ switch (itemType) {
+ case TYPE_STRING_ID_ITEM:
+ return new StringIdItem(dexFile);
+ case TYPE_TYPE_ID_ITEM:
+ return new TypeIdItem(dexFile);
+ case TYPE_PROTO_ID_ITEM:
+ return new ProtoIdItem(dexFile);
+ case TYPE_FIELD_ID_ITEM:
+ return new FieldIdItem(dexFile);
+ case TYPE_METHOD_ID_ITEM:
+ return new MethodIdItem(dexFile);
+ case TYPE_CLASS_DEF_ITEM:
+ return new ClassDefItem(dexFile);
+ case TYPE_TYPE_LIST:
+ return new TypeListItem(dexFile);
+ case TYPE_ANNOTATION_SET_REF_LIST:
+ return new AnnotationSetRefList(dexFile);
+ case TYPE_ANNOTATION_SET_ITEM:
+ return new AnnotationSetItem(dexFile);
+ case TYPE_CLASS_DATA_ITEM:
+ return new ClassDataItem(dexFile);
+ case TYPE_CODE_ITEM:
+ return new CodeItem(dexFile);
+ case TYPE_STRING_DATA_ITEM:
+ return new StringDataItem(dexFile);
+ case TYPE_DEBUG_INFO_ITEM:
+ return new DebugInfoItem(dexFile);
+ case TYPE_ANNOTATION_ITEM:
+ return new AnnotationItem(dexFile);
+ case TYPE_ENCODED_ARRAY_ITEM:
+ return new EncodedArrayItem(dexFile);
+ case TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+ return new AnnotationDirectoryItem(dexFile);
+ default:
+ assert false;
+ }
+ return null;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ItemType.java b/dexlib/src/main/java/org/jf/dexlib/ItemType.java
new file mode 100644
index 0000000..a8c7868
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ItemType.java
@@ -0,0 +1,123 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import java.util.TreeMap;
+
+/**
+ * Enumeration of all the top-level item types.
+ */
+public enum ItemType {
+ TYPE_HEADER_ITEM( 0x0000, 17, 4, "header_item"),
+ TYPE_STRING_ID_ITEM( 0x0001, 0, 4, "string_id_item"),
+ TYPE_TYPE_ID_ITEM( 0x0002, 1, 4, "type_id_item"),
+ TYPE_PROTO_ID_ITEM( 0x0003, 2, 4, "proto_id_item"),
+ TYPE_FIELD_ID_ITEM( 0x0004, 3, 4, "field_id_item"),
+ TYPE_METHOD_ID_ITEM( 0x0005, 4, 4, "method_id_item"),
+ TYPE_CLASS_DEF_ITEM( 0x0006, 5, 4, "class_def_item"),
+ TYPE_MAP_LIST( 0x1000, 16, 4, "map_list"),
+ TYPE_TYPE_LIST( 0x1001, 6, 4, "type_list"),
+ TYPE_ANNOTATION_SET_REF_LIST( 0x1002, 7, 4, "annotation_set_ref_list"),
+ TYPE_ANNOTATION_SET_ITEM( 0x1003, 8, 4, "annotation_set_item"),
+ TYPE_CLASS_DATA_ITEM( 0x2000, 9, 1, "class_data_item"),
+ TYPE_CODE_ITEM( 0x2001, 10, 4, "code_item"),
+ TYPE_STRING_DATA_ITEM( 0x2002, 11, 1, "string_data_item"),
+ TYPE_DEBUG_INFO_ITEM( 0x2003, 12, 1, "debug_info_item"),
+ TYPE_ANNOTATION_ITEM( 0x2004, 13, 1, "annotation_item"),
+ TYPE_ENCODED_ARRAY_ITEM( 0x2005, 14, 1, "encoded_array_item"),
+ TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, 15, 4, "annotations_directory_item");
+
+ /** A map to facilitate looking up an <code>ItemType</code> by ordinal */
+ private final static TreeMap<Integer, ItemType> itemTypeIntegerMap;
+
+ /** builds the <code>itemTypeIntegerMap</code> object */
+ static {
+ itemTypeIntegerMap = new TreeMap<Integer, ItemType>();
+
+ for (ItemType itemType: ItemType.values()) {
+ itemTypeIntegerMap.put(itemType.MapValue, itemType);
+ }
+ }
+
+
+
+ /**
+ * value when represented in a MapItem
+ */
+ public final int MapValue;
+
+ /**
+ * name of the type
+ */
+ public final String TypeName;
+
+ /**
+ * index for this item's section
+ */
+ public final int SectionIndex;
+
+ /**
+ * the alignment for this item type
+ */
+ public final int ItemAlignment;
+ /**
+ * Constructs an instance.
+ *
+ * @param mapValue value when represented in a MapItem
+ * @param sectionIndex index for this item's section
+ * @param itemAlignment the byte alignment required by this item
+ * @param typeName non-null; name of the type
+ */
+ private ItemType(int mapValue, int sectionIndex, int itemAlignment, String typeName) {
+ this.MapValue = mapValue;
+ this.SectionIndex = sectionIndex;
+ this.ItemAlignment = itemAlignment;
+ this.TypeName = typeName;
+ }
+
+ /**
+ * Converts an int value to the corresponding ItemType enum value,
+ * or null if the value isn't a valid ItemType value
+ *
+ * @param itemType the int value to convert to an ItemType
+ * @return the ItemType enum value corresponding to itemType, or null
+ * if not a valid ItemType value
+ */
+ public static ItemType fromInt(int itemType) {
+ return itemTypeIntegerMap.get(itemType);
+ }
+
+ /**
+ * Returns true if this is an indexed item, or false if its an offsetted item
+ * @return true if this is an indexed item, or false if its an offsetted item
+ */
+ public boolean isIndexedItem() {
+ return MapValue <= 0x1000;
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/MapItem.java b/dexlib/src/main/java/org/jf/dexlib/MapItem.java
new file mode 100644
index 0000000..e69a33a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/MapItem.java
@@ -0,0 +1,138 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+/**
+ * This item represents a map_list item from the dex specification. It contains a
+ * SectionInfo instance for every section in the DexFile, with the number of items
+ * in and offset of that section.
+ */
+public class MapItem extends Item<MapItem> {
+ /**
+ * This item is read in immediately after the HeaderItem, and the section info contained
+ * by this item is added to the ReadContext object, which is used when reading in the other
+ * sections in the dex file.
+ *
+ * This item should be placed last. It depends on the fact that the other sections
+ * in the file have been placed.
+ */
+
+ /**
+ * Create a new uninitialized <code>MapItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected MapItem(final DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ Section[] sections = dexFile.getOrderedSections();
+ //the list returned by getOrderedSections doesn't contain the header
+ //or map section, so add 2 to the length
+ return offset + 4 + (sections.length + 2) * 12;
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int size = in.readInt();
+
+ for (int i=0; i<size; i++) {
+ ItemType itemType = ItemType.fromInt(in.readShort());
+
+ //unused
+ in.readShort();
+
+ int sectionSize = in.readInt();
+ int sectionOffset = in.readInt();
+
+ readContext.addSection(itemType, sectionSize, sectionOffset);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ assert getOffset() > 0;
+ Section[] sections = dexFile.getOrderedSections();
+
+ out.annotate("map_size: 0x" + Integer.toHexString(sections.length + 2) + " (" +
+ Integer.toString(sections.length + 2) + ")");
+ out.writeInt(sections.length + 2);
+
+ int index = 0;
+ out.annotate(0, "[" + index++ + "]");
+ out.indent();
+ writeSectionInfo(out, ItemType.TYPE_HEADER_ITEM, 1, 0);
+ out.deindent();
+
+ for (Section section: dexFile.getOrderedSections()) {
+ out.annotate(0, "[" + index++ + "]");
+ out.indent();
+ writeSectionInfo(out, section.ItemType, section.getItems().size(), section.getOffset());
+ out.deindent();
+ }
+
+ out.annotate(0, "[" + index++ + "]");
+ out.indent();
+ writeSectionInfo(out, ItemType.TYPE_MAP_LIST, 1, dexFile.MapItem.getOffset());
+ out.deindent();
+ }
+
+ private void writeSectionInfo(AnnotatedOutput out, ItemType itemType, int sectionSize, int sectionOffset) {
+ if (out.annotates()) {
+ out.annotate(2, "item_type: " + itemType);
+ out.annotate(2, "unused");
+ out.annotate(4, "section_size: 0x" + Integer.toHexString(sectionSize) + " (" + sectionSize + ")");
+ out.annotate(4, "section_off: 0x" + Integer.toHexString(sectionOffset));
+ }
+
+ out.writeShort(itemType.MapValue);
+ out.writeShort(0);
+ out.writeInt(sectionSize);
+ out.writeInt(sectionOffset);
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_MAP_LIST;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(MapItem o) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "map_item";
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java
new file mode 100644
index 0000000..c3522c5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java
@@ -0,0 +1,256 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class MethodIdItem extends Item<MethodIdItem> implements Convertible<MethodIdItem> {
+ private int hashCode = 0;
+
+ private TypeIdItem classType;
+ private ProtoIdItem methodPrototype;
+ private StringIdItem methodName;
+
+ /**
+ * Creates a new uninitialized <code>MethodIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected MethodIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>MethodIdItem</code> for the given class, type and name
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the method is a member of
+ * @param methodPrototype the type of the method
+ * @param methodName the name of the method
+ */
+ private MethodIdItem(DexFile dexFile, TypeIdItem classType, ProtoIdItem methodPrototype, StringIdItem methodName) {
+ this(dexFile);
+ this.classType = classType;
+ this.methodPrototype = methodPrototype;
+ this.methodName = methodName;
+ }
+
+ /**
+ * Returns a <code>MethodIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the method is a member of
+ * @param methodPrototype the type of the method
+ * @param methodName the name of the method
+ * @return a <code>MethodIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static MethodIdItem internMethodIdItem(DexFile dexFile, TypeIdItem classType,
+ ProtoIdItem methodPrototype, StringIdItem methodName) {
+ MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName);
+ return dexFile.MethodIdsSection.intern(methodIdItem);
+ }
+
+ /**
+ * Looks up a <code>MethodIdItem</code> from the given <code>DexFile</code> for the given
+ * values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param classType the class that the method is a member of
+ * @param methodPrototype the type of the method
+ * @param methodName the name of the method
+ * @return a <code>MethodIdItem</code> from the given <code>DexFile</code> for the given
+ * values, or null if it doesn't exist
+ */
+ public static MethodIdItem lookupMethodIdItem(DexFile dexFile, TypeIdItem classType,
+ ProtoIdItem methodPrototype, StringIdItem methodName) {
+ MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName);
+ return dexFile.MethodIdsSection.getInternedItem(methodIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
+ methodPrototype = dexFile.ProtoIdsSection.getItemByIndex(in.readShort());
+ methodName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 8;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(2, "class_type: " + classType.getTypeDescriptor());
+ out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString());
+ out.annotate(4, "method_name: " + methodName.getStringValue());
+ }
+
+ int classIndex = classType.getIndex();
+ if (classIndex > 0xffff) {
+ throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " +
+ "defining class %s is too large", getMethodString(), classType.getTypeDescriptor()));
+ }
+ out.writeShort(classIndex);
+
+ int prototypeIndex = methodPrototype.getIndex();
+ if (prototypeIndex > 0xffff) {
+ throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " +
+ "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString()));
+ }
+ out.writeShort(prototypeIndex);
+
+ out.writeInt(methodName.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_METHOD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "method_id_item: " + getMethodString();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(MethodIdItem o) {
+ int result = classType.compareTo(o.classType);
+ if (result != 0) {
+ return result;
+ }
+
+ result = methodName.compareTo(o.methodName);
+ if (result != 0) {
+ return result;
+ }
+
+ return methodPrototype.compareTo(o.methodPrototype);
+ }
+
+ private String cachedMethodString = null;
+ /**
+ * @return a string formatted like LclassName;->methodName(TTTT..)R
+ */
+ public String getMethodString() {
+ if (cachedMethodString == null) {
+ String classType = this.classType.getTypeDescriptor();
+ String methodName = this.methodName.getStringValue();
+ String prototypeString = methodPrototype.getPrototypeString();
+
+ StringBuilder sb = new StringBuilder(classType.length() + methodName.length() + prototypeString.length() +
+ 2);
+ sb.append(classType);
+ sb.append("->");
+ sb.append(methodName);
+ sb.append(prototypeString);
+ cachedMethodString = sb.toString();
+ }
+ return cachedMethodString;
+ }
+
+ private String cachedShortMethodString = null;
+ /**
+ * @return a string formatted like methodName(TTTT..)R
+ */
+ public String getShortMethodString() {
+ if (cachedShortMethodString == null) {
+ String methodName = this.methodName.getStringValue();
+ String prototypeString = methodPrototype.getPrototypeString();
+
+ StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length());
+ sb.append(methodName);
+ sb.append(prototypeString);
+ cachedShortMethodString = sb.toString();
+ }
+ return cachedShortMethodString;
+ }
+
+ /**
+ * @return the method prototype
+ */
+ public ProtoIdItem getPrototype() {
+ return methodPrototype;
+ }
+
+ /**
+ * @return the name of the method
+ */
+ public StringIdItem getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * @return the class this method is a member of
+ */
+ public TypeIdItem getContainingClass() {
+ return classType;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = classType.hashCode();
+ hashCode = 31 * hashCode + methodPrototype.hashCode();
+ hashCode = 31 * hashCode + methodName.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ MethodIdItem other = (MethodIdItem)o;
+ return (classType == other.classType &&
+ methodPrototype == other.methodPrototype &&
+ methodName == other.methodName);
+ }
+
+ public MethodIdItem convert() {
+ return this;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java b/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java
new file mode 100644
index 0000000..581b8ac
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java
@@ -0,0 +1,76 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+
+import java.io.UnsupportedEncodingException;
+
+public class OdexDependencies {
+ public final int modificationTime;
+ public final int crc;
+ public final int dalvikBuild;
+
+ private final String[] dependencies;
+ private final byte[][] dependencyChecksums;
+
+ public OdexDependencies (Input in) {
+ modificationTime = in.readInt();
+ crc = in.readInt();
+ dalvikBuild = in.readInt();
+
+ int dependencyCount = in.readInt();
+
+ dependencies = new String[dependencyCount];
+ dependencyChecksums = new byte[dependencyCount][];
+
+ for (int i=0; i<dependencyCount; i++) {
+ int stringLength = in.readInt();
+
+ try {
+ dependencies[i] = new String(in.readBytes(stringLength), 0, stringLength-1, "US-ASCII");
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException(ex);
+ }
+ dependencyChecksums[i] = in.readBytes(20);
+ }
+ }
+
+ public int getDependencyCount() {
+ return dependencies.length;
+ }
+
+ public String getDependency(int index) {
+ return dependencies[index];
+ }
+
+ public byte[] getDependencyChecksum(int index) {
+ return dependencyChecksums[index].clone();
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/OdexHeader.java b/dexlib/src/main/java/org/jf/dexlib/OdexHeader.java
new file mode 100644
index 0000000..afc8f0b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/OdexHeader.java
@@ -0,0 +1,74 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+
+import java.util.Arrays;
+
+public class OdexHeader {
+
+ /**
+ * the possible file format magic numbers, represented as the low-order bytes of a string.
+ */
+ public static final byte[] MAGIC_35 = new byte[] {0x64, 0x65, 0x79, 0x0A, 0x30, 0x33, 0x35, 0x00}; //"dey\n035" + '\0';
+ public static final byte[] MAGIC_36 = new byte[] {0x64, 0x65, 0x79, 0x0A, 0x30, 0x33, 0x36, 0x00}; //"dey\n036" + '\0';
+
+ public final byte[] magic;
+ public final int dexOffset;
+ public final int dexLength;
+ public final int depsOffset;
+ public final int depsLength;
+ public final int auxOffset;
+ public final int auxLength;
+ public final int flags;
+
+ public final int version;
+
+ public OdexHeader(Input in) {
+ magic = in.readBytes(8);
+
+ if (Arrays.equals(MAGIC_35, magic)) {
+ version = 35;
+ } else if (Arrays.equals(MAGIC_36, magic)) {
+ version = 36;
+ } else {
+ throw new RuntimeException("The magic value is not one of the expected values");
+ }
+
+ dexOffset = in.readInt();
+ dexLength = in.readInt();
+ depsOffset = in.readInt();
+ depsLength = in.readInt();
+ auxOffset = in.readInt();
+ auxLength = in.readInt();
+ flags = in.readInt();
+ in.readInt(); //padding
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java b/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java
new file mode 100644
index 0000000..8ebb956
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/OffsettedSection.java
@@ -0,0 +1,53 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.Input;
+
+public class OffsettedSection<T extends Item> extends Section<T> {
+ public OffsettedSection(DexFile dexFile, ItemType itemType) {
+ super(dexFile, itemType);
+ }
+
+ public void readItems(Input in, ReadContext readContext) {
+
+ for (int i = 0; i < items.size(); i++) {
+ assert items.get(i) == null;
+
+ in.alignTo(ItemType.ItemAlignment);
+
+ T item = (T)ItemFactory.makeItem(ItemType, DexFile);
+
+ items.set(i, item);
+ item.readFrom(in, i, readContext);
+ }
+
+ readContext.setItemsForSection(ItemType, items);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java b/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java
new file mode 100644
index 0000000..a7b4a0a
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java
@@ -0,0 +1,231 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+public class ProtoIdItem extends Item<ProtoIdItem> {
+ private int hashCode = 0;
+
+ private StringIdItem shortyDescriptor;
+ private TypeIdItem returnType;
+ private TypeListItem parameters;
+
+ /**
+ * Creates a new uninitialized <code>ProtoIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected ProtoIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>ProtoIdItem</code> with the given values
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param returnType the return type
+ * @param parameters a <code>TypeListItem</code> containing a list of the parameter types
+ */
+ private ProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) {
+ this(dexFile);
+
+ String shortyString = returnType.toShorty();
+ if (parameters != null) {
+ shortyString += parameters.getShortyString();
+ }
+ this.shortyDescriptor = StringIdItem.internStringIdItem(dexFile, shortyString);
+ this.returnType = returnType;
+ this.parameters = parameters;
+ }
+
+ /**
+ * Returns a <code>ProtoIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param returnType the return type
+ * @param parameters a <code>TypeListItem</code> containing a list of the parameter types
+ * @return a <code>ProtoIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static ProtoIdItem internProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) {
+ ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters);
+ return dexFile.ProtoIdsSection.intern(protoIdItem);
+ }
+
+ /**
+ * Looks up the <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given
+ * values
+ * @param dexFile the <code>Dexfile</code> to find the type in
+ * @param returnType the return type
+ * @param parameters a <code>TypeListItem</code> containing a list of the parameter types
+ * @return a <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given
+ * values, or null if it doesn't exist
+ */
+ public static ProtoIdItem lookupProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) {
+ ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters);
+ return dexFile.ProtoIdsSection.getInternedItem(protoIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ shortyDescriptor = dexFile.StringIdsSection.getItemByIndex(in.readInt());
+ returnType = dexFile.TypeIdsSection.getItemByIndex(in.readInt());
+ parameters = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, in.readInt());
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 12;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, "shorty_descriptor: " + shortyDescriptor.getStringValue());
+ out.annotate(4, "return_type: " + returnType.getTypeDescriptor());
+
+ if (parameters == null) {
+ out.annotate(4, "parameters:");
+ } else {
+ out.annotate(4, "parameters: " + parameters.getTypeListString(""));
+ }
+ }
+
+ out.writeInt(shortyDescriptor.getIndex());
+ out.writeInt(returnType.getIndex());
+ out.writeInt(parameters == null?0:parameters.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_PROTO_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ProtoIdItem o) {
+ int result = returnType.compareTo(o.returnType);
+ if (result != 0) {
+ return result;
+ }
+
+ if (parameters == null) {
+ if (o.parameters == null) {
+ return 0;
+ }
+ return -1;
+ } else if (o.parameters == null) {
+ return 1;
+ }
+
+ return parameters.compareTo(o.parameters);
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "proto_id_item: " + getPrototypeString();
+ }
+
+ private String cachedPrototypeString = null;
+ /**
+ * @return a string in the format (TTTT..)R where TTTT.. are the parameter types and R is the return type
+ */
+ public String getPrototypeString() {
+ if (cachedPrototypeString == null) {
+ StringBuilder sb = new StringBuilder("(");
+ if (parameters != null) {
+ sb.append(parameters.getTypeListString(""));
+ }
+ sb.append(")");
+ sb.append(returnType.getTypeDescriptor());
+
+ cachedPrototypeString = sb.toString();
+ }
+ return cachedPrototypeString;
+ }
+
+ /**
+ * @return the return type of the method
+ */
+ public TypeIdItem getReturnType() {
+ return returnType;
+ }
+
+ /**
+ * @return a <code>TypeListItem</code> containing the method parameter types
+ */
+ public TypeListItem getParameters() {
+ return parameters;
+ }
+
+ /**
+ * @return the number of registers required for the parameters of this <code>ProtoIdItem</code>
+ */
+ public int getParameterRegisterCount() {
+ if (parameters == null) {
+ return 0;
+ } else {
+ return parameters.getRegisterCount();
+ }
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = returnType.hashCode();
+ hashCode = 31 * hashCode + (parameters==null?0:parameters.hashCode());
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ ProtoIdItem other = (ProtoIdItem)o;
+ return (returnType == other.returnType &&
+ parameters == other.parameters);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/ReadContext.java b/dexlib/src/main/java/org/jf/dexlib/ReadContext.java
new file mode 100644
index 0000000..a25f3a2
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/ReadContext.java
@@ -0,0 +1,200 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.ExceptionWithContext;
+import org.jf.dexlib.Util.SparseArray;
+
+import java.util.List;
+
+
+/**
+ * This class stores context information that is only needed when reading in a dex file
+ * Namely, it handles "pre-creating" items when an item needs to resolve some other item
+ * that it references, and keeps track of those pre-created items, so the corresponding section
+ * for the pre-created items uses them, instead of creating new items
+ */
+public class ReadContext {
+ private SparseArray<TypeListItem> typeListItems = new SparseArray<TypeListItem>(0);
+ private SparseArray<AnnotationSetRefList> annotationSetRefLists = new SparseArray<AnnotationSetRefList>(0);
+ private SparseArray<AnnotationSetItem> annotationSetItems = new SparseArray<AnnotationSetItem>(0);
+ private SparseArray<ClassDataItem> classDataItems = new SparseArray<ClassDataItem>(0);
+ private SparseArray<CodeItem> codeItems = new SparseArray<CodeItem>(0);
+ private SparseArray<StringDataItem> stringDataItems = new SparseArray<StringDataItem>(0);
+ private SparseArray<DebugInfoItem> debugInfoItems = new SparseArray<DebugInfoItem>(0);
+ private SparseArray<AnnotationItem> annotationItems = new SparseArray<AnnotationItem>(0);
+ private SparseArray<EncodedArrayItem> encodedArrayItems = new SparseArray<EncodedArrayItem>(0);
+ private SparseArray<AnnotationDirectoryItem> annotationDirectoryItems = new SparseArray<AnnotationDirectoryItem>(0);
+
+ private SparseArray[] itemsByType = new SparseArray[] {
+ null, //string_id_item
+ null, //type_id_item
+ null, //proto_id_item
+ null, //field_id_item
+ null, //method_id_item
+ null, //class_def_item
+ typeListItems,
+ annotationSetRefLists,
+ annotationSetItems,
+ classDataItems,
+ codeItems,
+ stringDataItems,
+ debugInfoItems,
+ annotationItems,
+ encodedArrayItems,
+ annotationDirectoryItems,
+ null, //map_list
+ null //header_item
+ };
+
+
+ /**
+ * The section sizes that are passed in while reading HeaderItem/MapItem, via the
+ * addSection method.
+ */
+ private int[] sectionSizes = new int[18];
+
+ /**
+ * The section offsets that are passed in while reading MapItem/HeaderItem, via the
+ * addSection method.
+ */
+ private int[] sectionOffsets = new int[18];
+
+ /**
+ * Creates a new ReadContext instance.
+ */
+ public ReadContext() {
+ for (int i=0; i<18; i++) {
+ sectionSizes[i] = -1;
+ sectionOffsets[i] = -1;
+ }
+ }
+
+ /**
+ * Gets the offsetted item of the specified type for the given offset. This method does not support retrieving an
+ * optional item where a value of 0 indicates "not present". Use getOptionalOffsettedItemByOffset instead.
+ *
+ * @param itemType The type of item to get
+ * @param offset The offset of the item
+ * @return the offsetted item of the specified type at the specified offset
+ */
+ public Item getOffsettedItemByOffset(ItemType itemType, int offset) {
+ assert !itemType.isIndexedItem();
+
+ SparseArray<Item> sa = itemsByType[itemType.SectionIndex];
+ Item item = sa.get(offset);
+ if (item == null) {
+ throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x",
+ itemType.TypeName, offset));
+ }
+ return item;
+ }
+
+ /**
+ * Gets the optional offsetted item of the specified type for the given offset
+ *
+ * @param itemType The type of item to get
+ * @param offset The offset of the item
+ * @return the offsetted item of the specified type at the specified offset, or null if the offset is 0
+ */
+ public Item getOptionalOffsettedItemByOffset(ItemType itemType, int offset) {
+ assert !itemType.isIndexedItem();
+
+ assert !itemType.isIndexedItem();
+
+ SparseArray<Item> sa = itemsByType[itemType.SectionIndex];
+ Item item = sa.get(offset);
+ if (item == null && offset != 0) {
+ throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x",
+ itemType.TypeName, offset));
+ }
+ return item;
+ }
+
+ /**
+ * Adds the size and offset information for the given offset
+ * @param itemType the item type of the section
+ * @param sectionSize the size of the section
+ * @param sectionOffset the offset of the section
+ */
+ public void addSection(final ItemType itemType, int sectionSize, int sectionOffset) {
+ int storedSectionSize = sectionSizes[itemType.SectionIndex];
+ if (storedSectionSize == -1) {
+ sectionSizes[itemType.SectionIndex] = sectionSize;
+ } else {
+ if (storedSectionSize != sectionSize) {
+ throw new RuntimeException("The section size in the header and map for item type "
+ + itemType + " do not match");
+ }
+ }
+
+ int storedSectionOffset = sectionOffsets[itemType.SectionIndex];
+ if (storedSectionOffset == -1) {
+ sectionOffsets[itemType.SectionIndex] = sectionOffset;
+ } else {
+ if (storedSectionOffset != sectionOffset) {
+ throw new RuntimeException("The section offset in the header and map for item type "
+ + itemType + " do not match");
+ }
+ }
+ }
+
+ /**
+ * Sets the items for the specified section. This should be called by an offsetted section
+ * after it is finished reading in all its items.
+ * @param itemType the item type of the section. This must be an offsetted item type
+ * @param items the full list of items in the section, ordered by offset
+ */
+ public void setItemsForSection(ItemType itemType, List<? extends Item> items) {
+ assert !itemType.isIndexedItem();
+
+ SparseArray<Item> sa = itemsByType[itemType.SectionIndex];
+
+ sa.ensureCapacity(items.size());
+ for (Item item: items) {
+ sa.append(item.getOffset(), item);
+ }
+ }
+
+ /**
+ * @param itemType the item type of the section
+ * @return the size of the given section as it was read in from the map item
+ */
+ public int getSectionSize(ItemType itemType) {
+ return sectionSizes[itemType.SectionIndex];
+ }
+
+ /**
+ * @param itemType the item type of the section
+ * @return the offset of the given section as it was read in from the map item
+ */
+ public int getSectionOffset(ItemType itemType) {
+ return sectionOffsets[itemType.SectionIndex];
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Section.java b/dexlib/src/main/java/org/jf/dexlib/Section.java
new file mode 100644
index 0000000..0c20a71
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Section.java
@@ -0,0 +1,221 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AlignmentUtils;
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public abstract class Section<T extends Item> {
+ /**
+ * A list of the items that this section contains.
+ * If the section has been placed, this list should be in the order that the items
+ * will written to the dex file
+ */
+ protected final ArrayList<T> items;
+
+ /**
+ * A HashMap of the items in this section. This is used when interning items, to determine
+ * if this section already has an item equivalent to the one that is being interned.
+ * Both the key and the value should be the same object
+ */
+ protected HashMap<T,T> uniqueItems = null;
+
+ /**
+ * The offset of this section within the <code>DexFile</code>
+ */
+ protected int offset = 0;
+
+ /**
+ * The type of item that this section holds
+ */
+ public final ItemType ItemType;
+
+ /**
+ * The <code>DexFile</code> that this section belongs to
+ */
+ public final DexFile DexFile;
+
+ /**
+ * Create a new section
+ * @param dexFile The <code>DexFile</code> that this section belongs to
+ * @param itemType The itemType that this section will hold
+ */
+ protected Section(DexFile dexFile, ItemType itemType) {
+ this.DexFile = dexFile;
+ items = new ArrayList<T>();
+ this.ItemType = itemType;
+ }
+
+ /**
+ * Finalize the location of all items, and place them starting at the given offset
+ * @param offset The offset where this section should be placed
+ * @return the offset of the byte immediate after the last item in this section
+ */
+ protected int placeAt(int offset) {
+ if (items.size() > 0) {
+ offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment);
+ assert !DexFile.getInplace() || offset == this.offset;
+ this.offset = offset;
+
+ for (int i=0; i < items.size(); i++) {
+ T item = items.get(i);
+ assert item != null;
+ offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment);
+ offset = item.placeAt(offset, i);
+ }
+ } else {
+ this.offset = 0;
+ }
+
+ return offset;
+ }
+
+ /**
+ * Write the items to the given <code>AnnotatedOutput</code>
+ * @param out the <code>AnnotatedOutput</code> object to write to
+ */
+ protected void writeTo(AnnotatedOutput out) {
+ out.annotate(0, " ");
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, this.ItemType.TypeName + " section");
+ out.annotate(0, "-----------------------------");
+ out.annotate(0, " ");
+
+ for (Item item: items) {
+ assert item!=null;
+ out.alignTo(ItemType.ItemAlignment);
+ item.writeTo(out);
+ out.annotate(0, " ");
+ }
+ }
+
+ /**
+ * Read the specified number of items from the given <code>Input</code> object
+ * @param size The number of items to read
+ * @param in The <code>Input</code> object to read from
+ * @param readContext a <code>ReadContext</code> object to hold information that is
+ * only needed while reading in a file
+ */
+ protected void readFrom(int size, Input in, ReadContext readContext) {
+ //readItems() expects that the list will already be the correct size, so add null items
+ //until we reach the specified size
+ items.ensureCapacity(size);
+ for (int i = items.size(); i < size; i++) {
+ items.add(null);
+ }
+
+ in.alignTo(ItemType.ItemAlignment);
+ offset = in.getCursor();
+
+ //call the subclass's method that actually reads in the items
+ readItems(in, readContext);
+ }
+
+ /**
+ * This method in the concrete item subclass should read in all the items from the given <code>Input</code>
+ * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the
+ * section, by other items requesting items from this section that they reference by index/offset)
+ * @param in the <code>Input</code>
+ * @param readContext a <code>ReadContext</code> object to hold information that is
+ * only needed while reading in a file
+ */
+ protected abstract void readItems(Input in, ReadContext readContext);
+
+ /**
+ * Gets the offset where the first item in this section is placed
+ * @return the ofset where the first item in this section is placed
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Gets a the items contained in this section as a read-only list
+ * @return A read-only <code>List</code> object containing the items in this section
+ */
+ public List<T> getItems() {
+ return Collections.unmodifiableList(items);
+ }
+
+ /**
+ * This method checks if an item that is equivalent to the given item has already been added. If found,
+ * it returns that item. If not found, it adds the given item to this section and returns it.
+ * @param item the item to intern
+ * @return An item from this section that is equivalent to the given item. It may or may not be the same
+ * as the item passed to this method.
+ */
+ protected T intern(T item) {
+ if (item == null) {
+ return null;
+ }
+ T internedItem = getInternedItem(item);
+ if (internedItem == null) {
+ uniqueItems.put(item, item);
+ items.add(item);
+ return item;
+ }
+ return internedItem;
+ }
+
+ /**
+ * Returns the interned item that is equivalent to the given item, or null
+ * @param item the item to check
+ * @return the interned item that is equivalent to the given item, or null
+ */
+ protected T getInternedItem(T item) {
+ if (uniqueItems == null) {
+ buildInternedItemMap();
+ }
+ return uniqueItems.get(item);
+ }
+
+ /**
+ * Builds the interned item map from the items that are in this section
+ */
+ private void buildInternedItemMap() {
+ uniqueItems = new HashMap<T,T>();
+ for (T item: items) {
+ assert item != null;
+ uniqueItems.put(item, item);
+ }
+ }
+
+ /**
+ * Sorts the items in the section
+ */
+ protected void sortSection() {
+ Collections.sort(items);
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java b/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java
new file mode 100644
index 0000000..a565d09
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java
@@ -0,0 +1,169 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Leb128Utils;
+import org.jf.dexlib.Util.Utf8Utils;
+
+public class StringDataItem extends Item<StringDataItem> {
+ private int hashCode = 0;
+
+ private String stringValue;
+
+ /**
+ * Creates a new uninitialized <code>StringDataItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected StringDataItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>StringDataItem</code> for the given string
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param stringValue The string value that this item represents
+ */
+ private StringDataItem(DexFile dexFile, String stringValue) {
+ super(dexFile);
+
+ this.stringValue = stringValue;
+ }
+
+ /**
+ * Returns a <code>StringDataItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param value The string value that this item represents
+ * @return a <code>StringDataItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static StringDataItem internStringDataItem(DexFile dexFile, String value) {
+ StringDataItem StringDataItem = new StringDataItem(dexFile, value);
+ return dexFile.StringDataSection.intern(StringDataItem);
+ }
+
+ /**
+ * Looks up the <code>StringDataItem</code> from the given <code>DexFile</code> for the given
+ * string value
+ * @param dexFile the <code>Dexfile</code> to find the string value in
+ * @param value The string value to look up
+ * @return a <code>StringDataItem</code> from the given <code>DexFile</code> for the given
+ * string value, or null if it doesn't exist
+ **/
+ public static StringDataItem lookupStringDataItem(DexFile dexFile, String value) {
+ StringDataItem StringDataItem = new StringDataItem(dexFile, value);
+ return dexFile.StringDataSection.getInternedItem(StringDataItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ in.readUnsignedLeb128(); //string length
+ stringValue = in.realNullTerminatedUtf8String();
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + Leb128Utils.unsignedLeb128Size(stringValue.length()) +
+ Utf8Utils.stringToUtf8Bytes(stringValue).length + 1;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ byte[] encodedValue = Utf8Utils.stringToUtf8Bytes(stringValue);
+ if (out.annotates()) {
+ out.annotate("string_size: 0x" + Integer.toHexString(stringValue.length()) + " (" + stringValue.length() +
+ ")");
+ out.writeUnsignedLeb128(stringValue.length());
+
+ out.annotate(encodedValue.length + 1, "string_data: \"" + Utf8Utils.escapeString(stringValue) + "\"");
+ } else {
+ out.writeUnsignedLeb128(stringValue.length());
+ }
+ out.write(encodedValue);
+ out.writeByte(0);
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_STRING_DATA_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "string_data_item: \"" + Utf8Utils.escapeString(getStringValue()) + "\"";
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(StringDataItem o) {
+ return getStringValue().compareTo(o.getStringValue());
+ }
+
+ /**
+ * Get the string value of this item as a <code>String</code>
+ * @return the string value of this item as a String
+ */
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ hashCode = getStringValue().hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ StringDataItem other = (StringDataItem)o;
+ return getStringValue().equals(other.getStringValue());
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java b/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java
new file mode 100644
index 0000000..61bb65e
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java
@@ -0,0 +1,177 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.Utf8Utils;
+
+import javax.annotation.Nullable;
+
+public class StringIdItem extends Item<StringIdItem> {
+ private StringDataItem stringDataItem;
+
+ /**
+ * Creates a new uninitialized <code>StringIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected StringIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>StringIdItem</code> for the given <code>StringDataItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param stringDataItem The <code>StringDataItem</code> that this <code>StringIdItem</code> represents
+ */
+ protected StringIdItem(DexFile dexFile, StringDataItem stringDataItem) {
+ super(dexFile);
+ this.stringDataItem = stringDataItem;
+ }
+
+ /**
+ * Returns a <code>StringIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item will belong to
+ * @param stringValue The string value that this item represents
+ * @return a <code>StringIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static StringIdItem internStringIdItem(DexFile dexFile, String stringValue) {
+ StringDataItem stringDataItem = StringDataItem.internStringDataItem(dexFile, stringValue);
+ if (stringDataItem == null) {
+ return null;
+ }
+ StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem);
+ return dexFile.StringIdsSection.intern(stringIdItem);
+ }
+
+ /**
+ * Looks up the <code>StringIdItem</code> from the given <code>DexFile</code> for the given
+ * string value
+ * @param dexFile the <code>Dexfile</code> to find the string value in
+ * @param stringValue The string value to look up
+ * @return a <code>StringIdItem</code> from the given <code>DexFile</code> for the given
+ * string value, or null if it doesn't exist
+ */
+ public static StringIdItem lookupStringIdItem(DexFile dexFile, String stringValue) {
+ StringDataItem stringDataItem = StringDataItem.lookupStringDataItem(dexFile, stringValue);
+ if (stringDataItem == null) {
+ return null;
+ }
+ StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem);
+ return dexFile.StringIdsSection.getInternedItem(stringIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int stringDataOffset = in.readInt();
+
+ stringDataItem = (StringDataItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_STRING_DATA_ITEM,
+ stringDataOffset);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, stringDataItem.getConciseIdentity());
+ }
+
+ out.writeInt(stringDataItem.getOffset());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_STRING_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "string_id_item: " + Utf8Utils.escapeString(getStringValue());
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(StringIdItem o) {
+ //sort by the string value
+ return getStringValue().compareTo(o.getStringValue());
+ }
+
+ /**
+ * Get the <code>String</code> value that this <code>StringIdItem</code> represents
+ * @return the <code>String</code> value that this <code>StringIdItem</code> represents
+ */
+ public String getStringValue() {
+ return stringDataItem.getStringValue();
+ }
+
+ /**
+ * Get the <code>String</code> value that the given <code>StringIdItem</code> represents
+ * @param stringIdItem The <code>StringIdItem</code> to get the string value of
+ * @return the <code>String</code> value that the given <code>StringIdItem</code> represents
+ */
+ @Nullable
+ public static String getStringValue(@Nullable StringIdItem stringIdItem) {
+ return stringIdItem==null?null:stringIdItem.getStringValue();
+ }
+
+ /**
+ * Get the <code>StringDataItem</code> that this <code>StringIdItem</code> references
+ * @return the <code>StringDataItem</code> that this <code>StringIdItem</code> references
+ */
+ public StringDataItem getStringDataItem() {
+ return stringDataItem;
+ }
+
+ @Override
+ public int hashCode() {
+ return stringDataItem.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ StringIdItem other = (StringIdItem)o;
+ return stringDataItem == other.stringDataItem;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java b/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java
new file mode 100644
index 0000000..6cc2b96
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java
@@ -0,0 +1,211 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+
+import javax.annotation.Nullable;
+
+public class TypeIdItem extends Item<TypeIdItem> {
+ private StringIdItem typeDescriptor;
+
+ /**
+ * Creates a new uninitialized <code>TypeIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected TypeIdItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>TypeIdItem</code> for the given <code>StringIdItem</code>
+ * @param dexFile The <code>DexFile</code> that this item will belong to
+ * @param typeDescriptor The <code>StringIdItem</code> containing the type descriptor that
+ * this <code>TypeIdItem</code> represents
+ */
+ private TypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) {
+ super(dexFile);
+ this.typeDescriptor = typeDescriptor;
+ }
+
+ /**
+ * Returns a <code>TypeIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item will belong to
+ * @param typeDescriptor The <code>StringIdItem</code> containing the type descriptor that
+ * this <code>TypeIdItem</code> represents
+ * @return a <code>TypeIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static TypeIdItem internTypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) {
+ TypeIdItem typeIdItem = new TypeIdItem(dexFile, typeDescriptor);
+ return dexFile.TypeIdsSection.intern(typeIdItem);
+ }
+
+ /**
+ * Returns a <code>TypeIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item will belong to
+ * @param typeDescriptor The string containing the type descriptor that this
+ * <code>TypeIdItem</code> represents
+ * @return a <code>TypeIdItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static TypeIdItem internTypeIdItem(DexFile dexFile, String typeDescriptor) {
+ StringIdItem stringIdItem = StringIdItem.internStringIdItem(dexFile, typeDescriptor);
+ if (stringIdItem == null) {
+ return null;
+ }
+ TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem);
+ return dexFile.TypeIdsSection.intern(typeIdItem);
+ }
+
+ /**
+ * Looks up the <code>TypeIdItem</code> from the given <code>DexFile</code> for the given
+ * type descriptor
+ * @param dexFile the <code>Dexfile</code> to find the type in
+ * @param typeDescriptor The string containing the type descriptor to look up
+ * @return a <code>TypeIdItem</code> from the given <code>DexFile</code> for the given
+ * type descriptor, or null if it doesn't exist
+ */
+ public static TypeIdItem lookupTypeIdItem(DexFile dexFile, String typeDescriptor) {
+ StringIdItem stringIdItem = StringIdItem.lookupStringIdItem(dexFile, typeDescriptor);
+ if (stringIdItem == null) {
+ return null;
+ }
+ TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem);
+ return dexFile.TypeIdsSection.getInternedItem(typeIdItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int stringIdIndex = in.readInt();
+ this.typeDescriptor = dexFile.StringIdsSection.getItemByIndex(stringIdIndex);
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ if (out.annotates()) {
+ out.annotate(4, typeDescriptor.getConciseIdentity());
+ }
+
+ out.writeInt(typeDescriptor.getIndex());
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_TYPE_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "type_id_item: " + getTypeDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(TypeIdItem o) {
+ //sort by the index of the StringIdItem
+ return typeDescriptor.compareTo(o.typeDescriptor);
+ }
+
+ /**
+ * Returns the type descriptor as a <code>String</code> for this type
+ * @return the type descriptor as a <code>String</code> for this type
+ */
+ public String getTypeDescriptor() {
+ return typeDescriptor.getStringValue();
+ }
+
+ /**
+ * Returns the type descriptor as a <code>String</code> for the given type
+ * @param typeIdItem The <code>TypeIdItem</code> to get the type descriptor of
+ * @return the type descriptor as a <code>String</code> for the gvien type
+ */
+ @Nullable
+ public static String getTypeDescriptor(@Nullable TypeIdItem typeIdItem) {
+ return typeIdItem==null?null:typeIdItem.getTypeDescriptor();
+ }
+
+ /**
+ * Returns the "shorty" representation of this type, used to create the shorty prototype string for a method
+ * @return the "shorty" representation of this type, used to create the shorty prototype string for a method
+ */
+ public String toShorty() {
+ String type = getTypeDescriptor();
+ if (type.length() > 1) {
+ return "L";
+ } else {
+ return type;
+ }
+ }
+
+ /**
+ * Calculates the number of 2-byte registers that an instance of this type requires
+ * @return The number of 2-byte registers that an instance of this type requires
+ */
+ public int getRegisterCount() {
+ String type = this.getTypeDescriptor();
+ /** Only the long and double primitive types are 2 words,
+ * everything else is a single word
+ */
+ if (type.charAt(0) == 'J' || type.charAt(0) == 'D') {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return typeDescriptor.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ TypeIdItem other = (TypeIdItem)o;
+ return typeDescriptor == other.typeDescriptor;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java b/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java
new file mode 100644
index 0000000..50262ea
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java
@@ -0,0 +1,288 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib;
+
+import org.jf.dexlib.Util.AnnotatedOutput;
+import org.jf.dexlib.Util.Input;
+import org.jf.dexlib.Util.ReadOnlyArrayList;
+
+import java.util.List;
+
+public class TypeListItem extends Item<TypeListItem> {
+ private int hashCode = 0;
+
+ private TypeIdItem[] typeList;
+
+ /**
+ * Creates a new uninitialized <code>TypeListItem</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ */
+ protected TypeListItem(DexFile dexFile) {
+ super(dexFile);
+ }
+
+ /**
+ * Creates a new <code>TypeListItem</code> for the given string
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param typeList A list of the types that this <code>TypeListItem</code> represents
+ */
+ private TypeListItem(DexFile dexFile, TypeIdItem[] typeList) {
+ super(dexFile);
+
+ this.typeList = typeList;
+ }
+
+ /**
+ * Returns a <code>TypeListItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ * @param dexFile The <code>DexFile</code> that this item belongs to
+ * @param typeList A list of the types that this <code>TypeListItem</code> represents
+ * @return a <code>TypeListItem</code> for the given values, and that has been interned into
+ * the given <code>DexFile</code>
+ */
+ public static TypeListItem internTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
+ TypeIdItem[] typeArray = new TypeIdItem[typeList.size()];
+ typeList.toArray(typeArray);
+ TypeListItem typeListItem = new TypeListItem(dexFile, typeArray);
+ return dexFile.TypeListsSection.intern(typeListItem);
+ }
+
+ /**
+ * Looks up the <code>TypeListItem</code> from the given <code>DexFile</code> for the given
+ * list of types
+ * @param dexFile the <code>Dexfile</code> to find the type in
+ * @param typeList A list of the types that the <code>TypeListItem</code> represents
+ * @return a <code>TypeListItem</code> from the given <code>DexFile</code> for the given
+ * list of types, or null if it doesn't exist
+ */
+ public static TypeListItem lookupTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
+ TypeIdItem[] typeArray = new TypeIdItem[typeList.size()];
+ typeList.toArray(typeArray);
+ TypeListItem typeListItem = new TypeListItem(dexFile, typeArray);
+ return dexFile.TypeListsSection.getInternedItem(typeListItem);
+ }
+
+ /** {@inheritDoc} */
+ protected void readItem(Input in, ReadContext readContext) {
+ int size = in.readInt();
+ typeList = new TypeIdItem[size];
+ for (int i=0; i<size; i++) {
+ int typeIndex = in.readShort();
+ typeList[i] = dexFile.TypeIdsSection.getItemByIndex(typeIndex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected int placeItem(int offset) {
+ return offset + 4 + typeList.length * 2;
+ }
+
+ /** {@inheritDoc} */
+ protected void writeItem(AnnotatedOutput out) {
+ //yes, the code to write the item is duplicated. This eliminates the need to iterate over the list twice
+
+ if (out.annotates()) {
+ out.annotate(4, "size: 0x" + Integer.toHexString(typeList.length) + " (" + typeList.length +")");
+
+ for (TypeIdItem typeIdItem: typeList) {
+ out.annotate(2, "type_id_item: " + typeIdItem.getTypeDescriptor());
+ }
+ }
+ out.writeInt(typeList.length);
+ for (TypeIdItem typeIdItem: typeList) {
+ int typeIndex = typeIdItem.getIndex();
+ if (typeIndex > 0xffff) {
+ throw new RuntimeException(String.format("Error writing type_list entry. The type index of " +
+ "type %s is too large", typeIdItem.getTypeDescriptor()));
+ }
+ out.writeShort(typeIndex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public ItemType getItemType() {
+ return ItemType.TYPE_TYPE_LIST;
+ }
+
+ /** {@inheritDoc} */
+ public String getConciseIdentity() {
+ return "type_list: " + getTypeListString("");
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(TypeListItem o) {
+ if (o == null) {
+ return 1;
+ }
+
+ int thisSize = typeList.length;
+ int otherSize = o.typeList.length;
+ int size = Math.min(thisSize, otherSize);
+
+ for (int i = 0; i < size; i++) {
+ int result = typeList[i].compareTo(o.typeList[i]);
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (thisSize < otherSize) {
+ return -1;
+ } else if (thisSize > otherSize) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return the number of registers required for this <code>TypeListItem</code>
+ */
+ public int getRegisterCount() {
+ int wordCount = 0;
+ for (TypeIdItem typeIdItem: typeList) {
+ wordCount += typeIdItem.getRegisterCount();
+ }
+ return wordCount;
+ }
+
+ /**
+ * @return a string consisting of the type descriptors in this <code>TypeListItem</code>
+ * that are separated by the given separator
+ * @param separator the separator between each type
+ */
+ public String getTypeListString(String separator) {
+ int size = 0;
+ for (TypeIdItem typeIdItem: typeList) {
+ size += typeIdItem.getTypeDescriptor().length();
+ size += separator.length();
+ }
+
+ StringBuilder sb = new StringBuilder(size);
+ for (TypeIdItem typeIdItem: typeList) {
+ sb.append(typeIdItem.getTypeDescriptor());
+ sb.append(separator);
+ }
+ if (typeList.length > 0) {
+ sb.delete(sb.length() - separator.length(), sb.length());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @return a string consisting of the shorty form of the type descriptors in this
+ * <code>TypeListItem</code> that are directly concatenated together
+ */
+ public String getShortyString() {
+ StringBuilder sb = new StringBuilder();
+ for (TypeIdItem typeIdItem: typeList) {
+ sb.append(typeIdItem.toShorty());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @param index the index of the <code>TypeIdItem</code> to get
+ * @return the <code>TypeIdItem</code> at the given index
+ */
+ public TypeIdItem getTypeIdItem(int index) {
+ return typeList[index];
+ }
+
+ /**
+ * @return the number of types in this <code>TypeListItem</code>
+ */
+ public int getTypeCount() {
+ return typeList.length;
+ }
+
+ /**
+ * @return an array of the <code>TypeIdItems</code> in this <code>TypeListItem</code>
+ */
+ public List<TypeIdItem> getTypes() {
+ return new ReadOnlyArrayList<TypeIdItem>(typeList);
+ }
+
+ /**
+ * Helper method to allow easier "inline" retrieval of of the list of TypeIdItems
+ * @param typeListItem the typeListItem to return the types of (can be null)
+ * @return an array of the <code>TypeIdItems</code> in the specified <code>TypeListItem</code>, or null if the
+ * TypeListItem is null
+ */
+ public static List<TypeIdItem> getTypes(TypeListItem typeListItem) {
+ return typeListItem==null?null:typeListItem.getTypes();
+ }
+
+ /**
+ * calculate and cache the hashcode
+ */
+ private void calcHashCode() {
+ int hashCode = 1;
+
+ for (TypeIdItem typeIdItem: typeList) {
+ hashCode = 31 * hashCode + typeIdItem.hashCode();
+ }
+ this.hashCode = hashCode;
+ }
+
+ @Override
+ public int hashCode() {
+ //there's a small possibility that the actual hash code will be 0. If so, we'll
+ //just end up recalculating it each time
+ if (hashCode == 0)
+ calcHashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this==o) {
+ return true;
+ }
+ if (o==null || !this.getClass().equals(o.getClass())) {
+ return false;
+ }
+
+ //This assumes that the referenced items have been interned in both objects.
+ //This is a valid assumption because all outside code must use the static
+ //"getInterned..." style methods to make new items, and any item created
+ //internally is guaranteed to be interned
+ TypeListItem other = (TypeListItem)o;
+ if (typeList.length != other.typeList.length) {
+ return false;
+ }
+
+ for (int i=0; i<typeList.length; i++) {
+ if (typeList[i] != other.typeList[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java b/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java
new file mode 100644
index 0000000..b0e2f79
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java
@@ -0,0 +1,178 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.util.HashMap;
+
+public enum AccessFlags
+{
+ PUBLIC(0x1, "public", true, true, true),
+ PRIVATE(0x2, "private", true, true, true),
+ PROTECTED(0x4, "protected", true, true, true),
+ STATIC(0x8, "static", true, true, true),
+ FINAL(0x10, "final", true, true, true),
+ SYNCHRONIZED(0x20, "synchronized", false, true, false),
+ VOLATILE(0x40, "volatile", false, false, true),
+ BRIDGE(0x40, "bridge", false, true, false),
+ TRANSIENT(0x80, "transient", false, false, true),
+ VARARGS(0x80, "varargs", false, true, false),
+ NATIVE(0x100, "native", false, true, false),
+ INTERFACE(0x200, "interface", true, false, false),
+ ABSTRACT(0x400, "abstract", true, true, false),
+ STRICTFP(0x800, "strictfp", false, true, false),
+ SYNTHETIC(0x1000, "synthetic", true, true, true),
+ ANNOTATION(0x2000, "annotation", true, false, false),
+ ENUM(0x4000, "enum", true, false, true),
+ CONSTRUCTOR(0x10000, "constructor", false, true, false),
+ DECLARED_SYNCHRONIZED(0x20000, "declared-synchronized", false, true, false);
+
+ private int value;
+ private String accessFlagName;
+ private boolean validForClass;
+ private boolean validForMethod;
+ private boolean validForField;
+
+ //cache the array of all AccessFlags, because .values() allocates a new array for every call
+ private final static AccessFlags[] allFlags;
+
+ private static HashMap<String, AccessFlags> accessFlagsByName;
+
+ static {
+ allFlags = AccessFlags.values();
+
+ accessFlagsByName = new HashMap<String, AccessFlags>();
+ for (AccessFlags accessFlag: allFlags) {
+ accessFlagsByName.put(accessFlag.accessFlagName, accessFlag);
+ }
+ }
+
+ private AccessFlags(int value, String accessFlagName, boolean validForClass, boolean validForMethod,
+ boolean validForField) {
+ this.value = value;
+ this.accessFlagName = accessFlagName;
+ this.validForClass = validForClass;
+ this.validForMethod = validForMethod;
+ this.validForField = validForField;
+ }
+
+ public static AccessFlags[] getAccessFlagsForClass(int accessFlagValue) {
+ int size = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) {
+ size++;
+ }
+ }
+
+ AccessFlags[] accessFlags = new AccessFlags[size];
+ int accessFlagsPosition = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) {
+ accessFlags[accessFlagsPosition++] = accessFlag;
+ }
+ }
+ return accessFlags;
+ }
+
+ private static String formatAccessFlags(AccessFlags[] accessFlags) {
+ int size = 0;
+ for (AccessFlags accessFlag: accessFlags) {
+ size += accessFlag.toString().length() + 1;
+ }
+
+ StringBuilder sb = new StringBuilder(size);
+ for (AccessFlags accessFlag: accessFlags) {
+ sb.append(accessFlag.toString());
+ sb.append(" ");
+ }
+ if (accessFlags.length > 0) {
+ sb.delete(sb.length() - 1, sb.length());
+ }
+ return sb.toString();
+ }
+
+ public static String formatAccessFlagsForClass(int accessFlagValue) {
+ return formatAccessFlags(getAccessFlagsForClass(accessFlagValue));
+ }
+
+ public static AccessFlags[] getAccessFlagsForMethod(int accessFlagValue) {
+ int size = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) {
+ size++;
+ }
+ }
+
+ AccessFlags[] accessFlags = new AccessFlags[size];
+ int accessFlagsPosition = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) {
+ accessFlags[accessFlagsPosition++] = accessFlag;
+ }
+ }
+ return accessFlags;
+ }
+
+ public static String formatAccessFlagsForMethod(int accessFlagValue) {
+ return formatAccessFlags(getAccessFlagsForMethod(accessFlagValue));
+ }
+
+ public static AccessFlags[] getAccessFlagsForField(int accessFlagValue) {
+ int size = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) {
+ size++;
+ }
+ }
+
+ AccessFlags[] accessFlags = new AccessFlags[size];
+ int accessFlagsPosition = 0;
+ for (AccessFlags accessFlag: allFlags) {
+ if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) {
+ accessFlags[accessFlagsPosition++] = accessFlag;
+ }
+ }
+ return accessFlags;
+ }
+
+ public static String formatAccessFlagsForField(int accessFlagValue) {
+ return formatAccessFlags(getAccessFlagsForField(accessFlagValue));
+ }
+
+ public static AccessFlags getAccessFlag(String accessFlag) {
+ return accessFlagsByName.get(accessFlag);
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public String toString() {
+ return accessFlagName;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java
new file mode 100644
index 0000000..0d9741b
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java
@@ -0,0 +1,41 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public abstract class AlignmentUtils {
+ public static int alignOffset(int offset, int alignment) {
+ int mask = alignment - 1;
+ assert (alignment >= 0) && ((mask & alignment) == 0);
+ return (offset + mask) & ~mask;
+ }
+
+ public static boolean isAligned(int offset, int alignment) {
+ return (offset % alignment) == 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java
new file mode 100644
index 0000000..928b200
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Interface for a binary output destination that may be augmented
+ * with textual annotations.
+ */
+public interface AnnotatedOutput
+ extends Output {
+ /**
+ * Get whether this instance will actually keep annotations.
+ *
+ * @return <code>true</code> iff annotations are being kept
+ */
+ public boolean annotates();
+
+ /**
+ * Get whether this instance is intended to keep verbose annotations.
+ * Annotators may use the result of calling this method to inform their
+ * annotation activity.
+ *
+ * @return <code>true</code> iff annotations are to be verbose
+ */
+ public boolean isVerbose();
+
+ /**
+ * Add an annotation for the subsequent output. Any previously
+ * open annotation will be closed by this call, and the new
+ * annotation marks all subsequent output until another annotation
+ * call.
+ *
+ * @param msg non-null; the annotation message
+ */
+ public void annotate(String msg);
+
+ /**
+ * Add an annotation for a specified amount of subsequent
+ * output. Any previously open annotation will be closed by this
+ * call. If there is already pending annotation from one or more
+ * previous calls to this method, the new call "consumes" output
+ * after all the output covered by the previous calls.
+ *
+ * @param amt &gt;= 0; the amount of output for this annotation to
+ * cover
+ * @param msg non-null; the annotation message
+ */
+ public void annotate(int amt, String msg);
+
+ /**
+ * End the most recent annotation. Subsequent output will be unannotated,
+ * until the next call to {@link #annotate}.
+ */
+ public void endAnnotation();
+
+ /**
+ * Get the maximum width of the annotated output. This is advisory:
+ * Implementations of this interface are encouraged to deal with too-wide
+ * output, but annotaters are encouraged to attempt to avoid exceeding
+ * the indicated width.
+ *
+ * @return &gt;= 1; the maximum width
+ */
+ public int getAnnotationWidth();
+
+ public void setIndentAmount(int indentAmount);
+ public void indent();
+ public void deindent();
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java
new file mode 100644
index 0000000..dab0837
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java
@@ -0,0 +1,71 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class ArrayUtils {
+ /**
+ * Utility method to sort two related arrays - that is, two arrays where the elements are related to each other
+ * by their position in the array. i.e. firstArray[0] is related to secondArray[0], firstArray[1] is related to
+ * secondArray[1], and so on. The first array is sorted based on its implementation of Comparable, and the 2nd
+ * array is sorted by it's related item in the first array, so that the same elements are still related to each
+ * other after the sort
+ * @param firstArray The first array, which contains the values to sort
+ * @param secondArray The second array, which will be sorted based on the values in the first array
+ * @param <A> The type of element in the first array
+ * @param <B> the type of element in the second array
+ */
+ public static <A extends Comparable<A>, B> void sortTwoArrays(A[] firstArray, B[] secondArray)
+ {
+ if (firstArray.length != secondArray.length) {
+ throw new RuntimeException("Both arrays must be of the same length");
+ }
+
+ class element
+ {
+ public A first;
+ public B second;
+ }
+
+ element[] elements = new element[firstArray.length];
+
+ Arrays.sort(elements, new Comparator<element>(){
+ public int compare(element a, element b) {
+ return a.first.compareTo(b.first);
+ }
+ });
+
+ for (int i=0; i<elements.length; i++) {
+ firstArray[i] = elements[i].first;
+ secondArray[i] = elements[i].second;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArray.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArray.java
new file mode 100644
index 0000000..bd9afa6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArray.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Wrapper for a <code>byte[]</code>, which provides read-only access and
+ * can "reveal" a partial slice of the underlying array.
+ *
+ * <b>Note:</b> Multibyte accessors all use big-endian order.
+ */
+public final class ByteArray {
+ /** non-null; underlying array */
+ private final byte[] bytes;
+
+ /** <code>&gt;= 0</code>; start index of the slice (inclusive) */
+ private final int start;
+
+ /** <code>&gt;= 0, &lt;= bytes.length</code>; size computed as
+ * <code>end - start</code> (in the constructor) */
+ private final int size;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes non-null; the underlying array
+ * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
+ * @param end <code>&gt;= start, &lt;= bytes.length</code>; end index of
+ * the slice (exclusive)
+ */
+ public ByteArray(byte[] bytes, int start, int end) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
+ }
+
+ if (end < start) {
+ throw new IllegalArgumentException("end < start");
+ }
+
+ if (end > bytes.length) {
+ throw new IllegalArgumentException("end > bytes.length");
+ }
+
+ this.bytes = bytes;
+ this.start = start;
+ this.size = end - start;
+ }
+
+ /**
+ * Constructs an instance from an entire <code>byte[]</code>.
+ *
+ * @param bytes non-null; the underlying array
+ */
+ public ByteArray(byte[] bytes) {
+ this(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Gets the size of the array, in bytes.
+ *
+ * @return &gt;= 0; the size
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns a slice (that is, a sub-array) of this instance.
+ *
+ * @param start <code>&gt;= 0</code>; start index of the slice (inclusive)
+ * @param end <code>&gt;= start, &lt;= size()</code>; end index of
+ * the slice (exclusive)
+ * @return non-null; the slice
+ */
+ public ByteArray slice(int start, int end) {
+ checkOffsets(start, end);
+ return new ByteArray(bytes, start + this.start, end + this.start);
+ }
+
+ /**
+ * Returns the offset into the given array represented by the given
+ * offset into this instance.
+ *
+ * @param offset offset into this instance
+ * @param bytes non-null; (alleged) underlying array
+ * @return corresponding offset into <code>bytes</code>
+ * @throws IllegalArgumentException thrown if <code>bytes</code> is
+ * not the underlying array of this instance
+ */
+ public int underlyingOffset(int offset, byte[] bytes) {
+ if (bytes != this.bytes) {
+ throw new IllegalArgumentException("wrong bytes");
+ }
+
+ return start + offset;
+ }
+
+ /**
+ * Gets the <code>signed byte</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; size(); offset to fetch
+ * @return <code>signed byte</code> at that offset
+ */
+ public int getByte(int off) {
+ checkOffsets(off, off + 1);
+ return getByte0(off);
+ }
+
+ /**
+ * Gets the <code>signed short</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
+ * @return <code>signed short</code> at that offset
+ */
+ public int getShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Gets the <code>signed int</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; (size() - 3); offset to fetch
+ * @return <code>signed int</code> at that offset
+ */
+ public int getInt(int off) {
+ checkOffsets(off, off + 4);
+ return (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ }
+
+ /**
+ * Gets the <code>signed long</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; (size() - 7); offset to fetch
+ * @return <code>signed int</code> at that offset
+ */
+ public long getLong(int off) {
+ checkOffsets(off, off + 8);
+ int part1 = (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ int part2 = (getByte0(off + 4) << 24) |
+ (getUnsignedByte0(off + 5) << 16) |
+ (getUnsignedByte0(off + 6) << 8) |
+ getUnsignedByte0(off + 7);
+
+ return (part2 & 0xffffffffL) | ((long) part1) << 32;
+ }
+
+ /**
+ * Gets the <code>unsigned byte</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; size(); offset to fetch
+ * @return <code>unsigned byte</code> at that offset
+ */
+ public int getUnsignedByte(int off) {
+ checkOffsets(off, off + 1);
+ return getUnsignedByte0(off);
+ }
+
+ /**
+ * Gets the <code>unsigned short</code> value at a particular offset.
+ *
+ * @param off <code>&gt;= 0, &lt; (size() - 1); offset to fetch
+ * @return <code>unsigned short</code> at that offset
+ */
+ public int getUnsignedShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Copies the contents of this instance into the given raw
+ * <code>byte[]</code> at the given offset. The given array must be
+ * large enough.
+ *
+ * @param out non-null; array to hold the output
+ * @param offset non-null; index into <code>out</code> for the first
+ * byte of output
+ */
+ public void getBytes(byte[] out, int offset) {
+ if ((out.length - offset) < size) {
+ throw new IndexOutOfBoundsException("(out.length - offset) < " +
+ "size()");
+ }
+
+ System.arraycopy(bytes, start, out, offset, size);
+ }
+
+ /**
+ * Checks a range of offsets for validity, throwing if invalid.
+ *
+ * @param s start offset (inclusive)
+ * @param e end offset (exclusive)
+ */
+ private void checkOffsets(int s, int e) {
+ if ((s < 0) || (e < s) || (e > size)) {
+ throw new IllegalArgumentException("bad range: " + s + ".." + e +
+ "; actual size " + size);
+ }
+ }
+
+ /**
+ * Gets the <code>signed byte</code> value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getByte0(int off) {
+ return bytes[start + off];
+ }
+
+ /**
+ * Gets the <code>unsigned byte</code> value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getUnsignedByte0(int off) {
+ return bytes[start + off] & 0xff;
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 0000000..ae33cb0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a <code>byte[]</code>.
+ *
+ * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.</p>
+ */
+public final class ByteArrayAnnotatedOutput
+ implements AnnotatedOutput {
+ /** default size for stretchy instances */
+ private static final int DEFAULT_SIZE = 1000;
+
+ /**
+ * whether the instance is stretchy, that is, whether its array
+ * may be resized to increase capacity
+ */
+ private final boolean stretchy;
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** &gt;= 0; current output cursor */
+ private int cursor;
+
+ /** whether annotations are to be verbose */
+ private boolean verbose;
+
+ /**
+ * null-ok; list of annotations, or <code>null</code> if this instance
+ * isn't keeping them
+ */
+ private ArrayList<Annotation> annotations;
+
+ /** &gt;= 40 (if used); the desired maximum annotation width */
+ private int annotationWidth;
+
+ /**
+ * &gt;= 8 (if used); the number of bytes of hex output to use
+ * in annotations
+ */
+ private int hexCols;
+
+ private int currentIndent = 0;
+ private int indentAmount = 2;
+
+ /**
+ * Constructs an instance with a fixed maximum size. Note that the
+ * given array is the only one that will be used to store data. In
+ * particular, no reallocation will occur in order to expand the
+ * capacity of the resulting instance. Also, the constructed
+ * instance does not keep annotations by default.
+ *
+ * @param data non-null; data array to use for output
+ */
+ public ByteArrayAnnotatedOutput(byte[] data) {
+ this(data, false);
+ }
+
+ /**
+ * Constructs a "stretchy" instance. The underlying array may be
+ * reallocated. The constructed instance does not keep annotations
+ * by default.
+ */
+ public ByteArrayAnnotatedOutput() {
+ this(new byte[DEFAULT_SIZE], true);
+ }
+
+ /**
+ * Internal constructor.
+ *
+ * @param data non-null; data array to use for output
+ * @param stretchy whether the instance is to be stretchy
+ */
+ private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.stretchy = stretchy;
+ this.data = data;
+ this.cursor = 0;
+ this.verbose = false;
+ this.annotations = null;
+ this.annotationWidth = 0;
+ this.hexCols = 0;
+ }
+
+ /**
+ * Gets the underlying <code>byte[]</code> of this instance, which
+ * may be larger than the number of bytes written
+ *
+ * @see #toByteArray
+ *
+ * @return non-null; the <code>byte[]</code>
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /**
+ * Constructs and returns a new <code>byte[]</code> that contains
+ * the written contents exactly (that is, with no extra unwritten
+ * bytes at the end).
+ *
+ * @see #getArray
+ *
+ * @return non-null; an appropriately-constructed array
+ */
+ public byte[] toByteArray() {
+ byte[] result = new byte[cursor];
+ System.arraycopy(data, 0, result, 0, cursor);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeByte(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 1;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeShort(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 2;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeInt(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 4;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ data[writeAt + 2] = (byte) (value >> 16);
+ data[writeAt + 3] = (byte) (value >> 24);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeLong(long value) {
+ int writeAt = cursor;
+ int end = writeAt + 8;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ int half = (int) value;
+ data[writeAt] = (byte) half;
+ data[writeAt + 1] = (byte) (half >> 8);
+ data[writeAt + 2] = (byte) (half >> 16);
+ data[writeAt + 3] = (byte) (half >> 24);
+
+ half = (int) (value >> 32);
+ data[writeAt + 4] = (byte) half;
+ data[writeAt + 5] = (byte) (half >> 8);
+ data[writeAt + 6] = (byte) (half >> 16);
+ data[writeAt + 7] = (byte) (half >> 24);
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public int writeUnsignedLeb128(int value) {
+ long remaining = (value & 0xFFFFFFFFL) >> 7;
+ long lValue = value;
+ int count = 0;
+
+ while (remaining != 0) {
+ writeByte((int)(lValue & 0x7f) | 0x80);
+ lValue = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ writeByte((int)(lValue & 0x7f));
+ return count + 1;
+ }
+
+ /** {@inheritDoc} */
+ public int writeSignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /** {@inheritDoc} */
+ public void write(ByteArray bytes) {
+ int blen = bytes.size();
+ int writeAt = cursor;
+ int end = writeAt + blen;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ bytes.getBytes(data, writeAt);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes, int offset, int length) {
+ int writeAt = cursor;
+ int end = writeAt + length;
+ int bytesEnd = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+ throw new IndexOutOfBoundsException("bytes.length " +
+ bytes.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ System.arraycopy(bytes, offset, data, writeAt, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes) {
+ write(bytes, 0, bytes.length);
+ }
+
+ /** {@inheritDoc} */
+ public void writeZeroes(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ int end = cursor + count;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int mask = alignment - 1;
+
+ if ((alignment < 0) || ((mask & alignment) != 0)) {
+ throw new IllegalArgumentException("bogus alignment");
+ }
+
+ int end = (cursor + mask) & ~mask;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public boolean annotates() {
+ return (annotations != null);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+ annotations.add(new Annotation(cursor, msg, currentIndent));
+ }
+
+ public void indent() {
+ currentIndent++;
+ }
+
+ public void deindent() {
+ currentIndent--;
+ if (currentIndent < 0) {
+ currentIndent = 0;
+ }
+ }
+
+ public void setIndentAmount(int indentAmount) {
+ this.indentAmount = indentAmount;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(int amt, String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+
+ int asz = annotations.size();
+ int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+ int startAt;
+
+ if (lastEnd <= cursor) {
+ startAt = cursor;
+ } else {
+ startAt = lastEnd;
+ }
+
+ annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent));
+ }
+
+ /** {@inheritDoc} */
+ public void endAnnotation() {
+ if (annotations == null) {
+ return;
+ }
+
+ int sz = annotations.size();
+
+ if (sz != 0) {
+ annotations.get(sz - 1).setEndIfUnset(cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getAnnotationWidth() {
+ int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+ return annotationWidth - leftWidth;
+ }
+
+ /**
+ * Indicates that this instance should keep annotations. This method may
+ * be called only once per instance, and only before any data has been
+ * written to the it.
+ *
+ * @param annotationWidth &gt;= 40; the desired maximum annotation width
+ * @param verbose whether or not to indicate verbose annotations
+ */
+ public void enableAnnotations(int annotationWidth, boolean verbose) {
+ if ((annotations != null) || (cursor != 0)) {
+ throw new RuntimeException("cannot enable annotations");
+ }
+
+ if (annotationWidth < 40) {
+ throw new IllegalArgumentException("annotationWidth < 40");
+ }
+
+ int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+
+ this.annotations = new ArrayList<Annotation>(1000);
+ this.annotationWidth = annotationWidth;
+ this.hexCols = hexCols;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Finishes up annotation processing. This closes off any open
+ * annotations and removes annotations that don't refer to written
+ * data.
+ */
+ public void finishAnnotating() {
+ // Close off the final annotation, if any.
+ endAnnotation();
+
+ // Remove annotations that refer to unwritten data.
+ if (annotations != null) {
+ int asz = annotations.size();
+ while (asz > 0) {
+ Annotation last = annotations.get(asz - 1);
+ if (last.getStart() > cursor) {
+ annotations.remove(asz - 1);
+ asz--;
+ } else if (last.getEnd() > cursor) {
+ last.setEnd(cursor);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the annotated content of this instance to the given writer.
+ *
+ * @param out non-null; where to write to
+ */
+ public void writeAnnotationsTo(Writer out) throws IOException {
+ int width2 = getAnnotationWidth();
+ int width1 = annotationWidth - width2 - 1;
+
+ StringBuilder padding = new StringBuilder();
+ for (int i=0; i<1000; i++) {
+ padding.append(' ');
+ }
+
+ TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
+ Writer left = twoc.getLeft();
+ Writer right = twoc.getRight();
+ int leftAt = 0; // left-hand byte output cursor
+ int rightAt = 0; // right-hand annotation index
+ int rightSz = annotations.size();
+
+ while ((leftAt < cursor) && (rightAt < rightSz)) {
+ Annotation a = annotations.get(rightAt);
+ int start = a.getStart();
+ int end;
+ String text;
+
+ if (leftAt < start) {
+ // This is an area with no annotation.
+ end = start;
+ start = leftAt;
+ text = "";
+ } else {
+ // This is an area with an annotation.
+ end = a.getEnd();
+ text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText();
+ rightAt++;
+ }
+
+ left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
+ right.write(text);
+ twoc.flush();
+ leftAt = end;
+ }
+
+ if (leftAt < cursor) {
+ // There is unannotated output at the end.
+ left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
+ hexCols, 6));
+ }
+
+ while (rightAt < rightSz) {
+ // There are zero-byte annotations at the end.
+ right.write(annotations.get(rightAt).getText());
+ rightAt++;
+ }
+
+ twoc.flush();
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to write past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to write past the end");
+ }
+
+ /**
+ * Reallocates the underlying array if necessary. Calls to this method
+ * should be guarded by a test of {@link #stretchy}.
+ *
+ * @param desiredSize &gt;= 0; the desired minimum total size of the array
+ */
+ private void ensureCapacity(int desiredSize) {
+ if (data.length < desiredSize) {
+ byte[] newData = new byte[desiredSize * 2 + 1000];
+ System.arraycopy(data, 0, newData, 0, cursor);
+ data = newData;
+ }
+ }
+
+ /**
+ * Annotation on output.
+ */
+ private static class Annotation {
+ /** &gt;= 0; start of annotated range (inclusive) */
+ private final int start;
+
+ /**
+ * &gt;= 0; end of annotated range (exclusive);
+ * <code>Integer.MAX_VALUE</code> if unclosed
+ */
+ private int end;
+
+ /** non-null; annotation text */
+ private final String text;
+
+ private int indent;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start &gt;= 0; start of annotated range
+ * @param end &gt;= start; end of annotated range (exclusive) or
+ * <code>Integer.MAX_VALUE</code> if unclosed
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, int end, String text, int indent) {
+ this.start = start;
+ this.end = end;
+ this.text = text;
+ this.indent = indent;
+ }
+
+ /**
+ * Constructs an instance. It is initally unclosed.
+ *
+ * @param start &gt;= 0; start of annotated range
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, String text, int indent) {
+ this(start, Integer.MAX_VALUE, text, indent);
+ }
+
+ /**
+ * Sets the end as given, but only if the instance is unclosed;
+ * otherwise, do nothing.
+ *
+ * @param end &gt;= start; the end
+ */
+ public void setEndIfUnset(int end) {
+ if (this.end == Integer.MAX_VALUE) {
+ this.end = end;
+ }
+ }
+
+ /**
+ * Sets the end as given.
+ *
+ * @param end &gt;= start; the end
+ */
+ public void setEnd(int end) {
+ this.end = end;
+ }
+
+ /**
+ * Gets the start.
+ *
+ * @return the start
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end.
+ *
+ * @return the end
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the text.
+ *
+ * @return non-null; the text
+ */
+ public String getText() {
+ return text;
+ }
+
+ public int getIndent() {
+ return indent;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java
new file mode 100644
index 0000000..e74a62e
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Implementation of {@link Input} which reads the data from a
+ * <code>byte[]</code> instance.
+ *
+ * <p><b>Note:</b> As per the {@link Input } interface, multi-byte
+ * reads all use little-endian order.</p>
+ */
+public class ByteArrayInput
+ implements Input {
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** &gt;= 0; current read cursor */
+ private int cursor;
+
+ /**
+ * Constructs an instance with the given data
+ *
+ * @param data non-null; data array to use for input
+ */
+ public ByteArrayInput(byte[] data) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.data = data;
+ this.cursor = 0;
+ }
+
+ /**
+ * Gets the underlying <code>byte[]</code> of this instance
+ *
+ * @return non-null; the <code>byte[]</code>
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void setCursor(int cursor) {
+ if (cursor < 0 || cursor >= data.length)
+ throw new IndexOutOfBoundsException("The provided cursor value " +
+ "is not within the bounds of this instance's data array");
+ this.cursor = cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public byte readByte() {
+ return data[cursor++];
+ }
+
+ /** {@inheritDoc} */
+ public int readShort() {
+ int readAt = cursor;
+ int result = ((data[readAt++] & 0xff) +
+ ((data[readAt++] & 0xff) << 8));
+ cursor = readAt;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int readInt() {
+ int readAt = cursor;
+ int result = (data[readAt++] & 0xff) +
+ ((data[readAt++] & 0xff) << 8) +
+ ((data[readAt++] & 0xff) << 16) +
+ ((data[readAt++] & 0xff) << 24);
+ cursor = readAt;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public long readLong() {
+ int readAt = cursor;
+
+ long result = (data[readAt++] & 0xffL) |
+ ((data[readAt++] & 0xffL) << 8) |
+ ((data[readAt++] & 0xffL) << 16) |
+ ((data[readAt++] & 0xffL) << 24) |
+ ((data[readAt++] & 0xffL) << 32) |
+ ((data[readAt++] & 0xffL) << 40) |
+ ((data[readAt++] & 0xffL) << 48) |
+ ((data[readAt++] & 0xffL) << 56);
+ cursor = readAt;
+ return result;
+ }
+
+
+ /** {@inheritDoc} */
+ public int readUnsignedOrSignedLeb128() {
+ int end = cursor;
+ int currentByteValue;
+ int result;
+
+ result = data[end++] & 0xff;
+ if (result > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 14;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 21;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ if (currentByteValue > 0x0f) {
+ throwInvalidLeb();
+ }
+ result |= currentByteValue << 28;
+ }
+ }
+ }
+ } else {
+ cursor = end;
+ return result;
+ }
+
+ cursor = end;
+
+ //If the last byte is 0, then this was an unsigned value (incorrectly) written in a signed format
+ //The caller wants to know if this is the case, so we'll return the negated value instead
+ //If there was only a single byte that had a value of 0, then we would have returned in the above
+ //"else"
+ if (data[end-1] == 0) {
+ return ~result;
+ }
+ return result;
+ }
+
+
+
+
+ /** {@inheritDoc} */
+ public int readUnsignedLeb128() {
+ int end = cursor;
+ int currentByteValue;
+ int result;
+
+ result = data[end++] & 0xff;
+ if (result > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 14;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 21;
+ if (currentByteValue > 0x7f) {
+ currentByteValue = data[end++] & 0xff;
+ if (currentByteValue > 0x0f) {
+ throwInvalidLeb();
+ }
+ result |= currentByteValue << 28;
+ }
+ }
+ }
+ }
+
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int readSignedLeb128() {
+ int end = cursor;
+ int currentByteValue;
+ int result;
+
+ result = data[end++] & 0xff;
+ if (result <= 0x7f) {
+ result = (result << 25) >> 25;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7);
+ if (currentByteValue <= 0x7f) {
+ result = (result << 18) >> 18;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 14;
+ if (currentByteValue <= 0x7f) {
+ result = (result << 11) >> 11;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ result |= (currentByteValue & 0x7f) << 21;
+ if (currentByteValue <= 0x7f) {
+ result = (result << 4) >> 4;
+ } else {
+ currentByteValue = data[end++] & 0xff;
+ if (currentByteValue > 0x0f) {
+ throwInvalidLeb();
+ }
+ result |= currentByteValue << 28;
+ }
+ }
+ }
+ }
+
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public void read(byte[] bytes, int offset, int length) {
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ System.arraycopy(data, cursor, bytes, offset, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void read(byte[] bytes) {
+ int length = bytes.length;
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ System.arraycopy(data, cursor, bytes, 0, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public byte[] readBytes(int length) {
+ int end = cursor + length;
+
+ if (end > data.length) {
+ throwBounds();
+ }
+
+ byte[] result = new byte[length];
+ System.arraycopy(data, cursor, result, 0, length);
+ cursor = end;
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public String realNullTerminatedUtf8String() {
+ int startPosition = cursor;
+ while (data[cursor] != 0) {
+ cursor++;
+ }
+ int byteCount = cursor - startPosition;
+
+ //skip the terminating null
+ cursor++;
+
+ return Utf8Utils.utf8BytesToString(data, startPosition, byteCount);
+ }
+
+ /** {@inheritDoc} */
+ public void skipBytes(int count) {
+ cursor += count;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ cursor = AlignmentUtils.alignOffset(cursor, alignment);
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to read past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to read past the end");
+ }
+
+ /**
+ * Throws the exception for when an invalid LEB128 value is encountered
+ */
+ private static void throwInvalidLeb() {
+ throw new RuntimeException("invalid LEB128 integer encountered");
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java
new file mode 100644
index 0000000..f2a1f70
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.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.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a <code>byte[]</code>.
+ *
+ * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.</p>
+ */
+public final class ByteArrayOutput implements Output
+{
+ /** default size for stretchy instances */
+ private static final int DEFAULT_SIZE = 1000;
+
+ /**
+ * whether the instance is stretchy, that is, whether its array
+ * may be resized to increase capacity
+ */
+ private final boolean stretchy;
+
+ /** non-null; the data itself */
+ private byte[] data;
+
+ /** &gt;= 0; current output cursor */
+ private int cursor;
+
+ /** whether annotations are to be verbose */
+ private boolean verbose;
+
+ /**
+ * null-ok; list of annotations, or <code>null</code> if this instance
+ * isn't keeping them
+ */
+ private ArrayList<Annotation> annotations;
+
+ /** &gt;= 40 (if used); the desired maximum annotation width */
+ private int annotationWidth;
+
+ /**
+ * &gt;= 8 (if used); the number of bytes of hex output to use
+ * in annotations
+ */
+ private int hexCols;
+
+ /**
+ * Constructs an instance with a fixed maximum size. Note that the
+ * given array is the only one that will be used to store data. In
+ * particular, no reallocation will occur in order to expand the
+ * capacity of the resulting instance. Also, the constructed
+ * instance does not keep annotations by default.
+ *
+ * @param data non-null; data array to use for output
+ */
+ public ByteArrayOutput(byte[] data) {
+ this(data, false);
+ }
+
+ /**
+ * Constructs a "stretchy" instance. The underlying array may be
+ * reallocated. The constructed instance does not keep annotations
+ * by default.
+ */
+ public ByteArrayOutput() {
+ this(new byte[DEFAULT_SIZE], true);
+ }
+
+ /**
+ * Internal constructor.
+ *
+ * @param data non-null; data array to use for output
+ * @param stretchy whether the instance is to be stretchy
+ */
+ private ByteArrayOutput(byte[] data, boolean stretchy) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.stretchy = stretchy;
+ this.data = data;
+ this.cursor = 0;
+ this.verbose = false;
+ this.annotations = null;
+ this.annotationWidth = 0;
+ this.hexCols = 0;
+ }
+
+ /**
+ * Gets the underlying <code>byte[]</code> of this instance, which
+ * may be larger than the number of bytes written
+ *
+ * @see #toByteArray
+ *
+ * @return non-null; the <code>byte[]</code>
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /**
+ * Constructs and returns a new <code>byte[]</code> that contains
+ * the written contents exactly (that is, with no extra unwritten
+ * bytes at the end).
+ *
+ * @see #getArray
+ *
+ * @return non-null; an appropriately-constructed array
+ */
+ public byte[] toByteArray() {
+ byte[] result = new byte[cursor];
+ System.arraycopy(data, 0, result, 0, cursor);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeByte(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 1;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeShort(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 2;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeInt(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 4;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ data[writeAt + 2] = (byte) (value >> 16);
+ data[writeAt + 3] = (byte) (value >> 24);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeLong(long value) {
+ int writeAt = cursor;
+ int end = writeAt + 8;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ int half = (int) value;
+ data[writeAt] = (byte) half;
+ data[writeAt + 1] = (byte) (half >> 8);
+ data[writeAt + 2] = (byte) (half >> 16);
+ data[writeAt + 3] = (byte) (half >> 24);
+
+ half = (int) (value >> 32);
+ data[writeAt + 4] = (byte) half;
+ data[writeAt + 5] = (byte) (half >> 8);
+ data[writeAt + 6] = (byte) (half >> 16);
+ data[writeAt + 7] = (byte) (half >> 24);
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public int writeUnsignedLeb128(int value) {
+ int remaining = value >>> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ writeByte((value & 0x7f) | 0x80);
+ value = remaining;
+ remaining >>>= 7;
+ count++;
+ }
+
+ writeByte(value & 0x7f);
+ return count + 1;
+ }
+
+ /** {@inheritDoc} */
+ public int writeSignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /** {@inheritDoc} */
+ public void write(ByteArray bytes) {
+ int blen = bytes.size();
+ int writeAt = cursor;
+ int end = writeAt + blen;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ bytes.getBytes(data, writeAt);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes, int offset, int length) {
+ int writeAt = cursor;
+ int end = writeAt + length;
+ int bytesEnd = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+ throw new IndexOutOfBoundsException("bytes.length " +
+ bytes.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ System.arraycopy(bytes, offset, data, writeAt, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes) {
+ write(bytes, 0, bytes.length);
+ }
+
+ /** {@inheritDoc} */
+ public void writeZeroes(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ int end = cursor + count;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int end = AlignmentUtils.alignOffset(cursor, alignment);
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public boolean annotates() {
+ return (annotations != null);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+ annotations.add(new Annotation(cursor, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(int amt, String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+
+ int asz = annotations.size();
+ int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+ int startAt;
+
+ if (lastEnd <= cursor) {
+ startAt = cursor;
+ } else {
+ startAt = lastEnd;
+ }
+
+ annotations.add(new Annotation(startAt, startAt + amt, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void endAnnotation() {
+ if (annotations == null) {
+ return;
+ }
+
+ int sz = annotations.size();
+
+ if (sz != 0) {
+ annotations.get(sz - 1).setEndIfUnset(cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getAnnotationWidth() {
+ int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+ return annotationWidth - leftWidth;
+ }
+
+ /**
+ * Indicates that this instance should keep annotations. This method may
+ * be called only once per instance, and only before any data has been
+ * written to the it.
+ *
+ * @param annotationWidth &gt;= 40; the desired maximum annotation width
+ * @param verbose whether or not to indicate verbose annotations
+ */
+ public void enableAnnotations(int annotationWidth, boolean verbose) {
+ if ((annotations != null) || (cursor != 0)) {
+ throw new RuntimeException("cannot enable annotations");
+ }
+
+ if (annotationWidth < 40) {
+ throw new IllegalArgumentException("annotationWidth < 40");
+ }
+
+ int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+
+ this.annotations = new ArrayList<Annotation>(1000);
+ this.annotationWidth = annotationWidth;
+ this.hexCols = hexCols;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Finishes up annotation processing. This closes off any open
+ * annotations and removes annotations that don't refer to written
+ * data.
+ */
+ public void finishAnnotating() {
+ // Close off the final annotation, if any.
+ endAnnotation();
+
+ // Remove annotations that refer to unwritten data.
+ if (annotations != null) {
+ int asz = annotations.size();
+ while (asz > 0) {
+ Annotation last = annotations.get(asz - 1);
+ if (last.getStart() > cursor) {
+ annotations.remove(asz - 1);
+ asz--;
+ } else if (last.getEnd() > cursor) {
+ last.setEnd(cursor);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to write past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to write past the end");
+ }
+
+ /**
+ * Reallocates the underlying array if necessary. Calls to this method
+ * should be guarded by a test of {@link #stretchy}.
+ *
+ * @param desiredSize &gt;= 0; the desired minimum total size of the array
+ */
+ private void ensureCapacity(int desiredSize) {
+ if (data.length < desiredSize) {
+ byte[] newData = new byte[desiredSize * 2 + 1000];
+ System.arraycopy(data, 0, newData, 0, cursor);
+ data = newData;
+ }
+ }
+
+ /**
+ * Annotation on output.
+ */
+ private static class Annotation {
+ /** &gt;= 0; start of annotated range (inclusive) */
+ private final int start;
+
+ /**
+ * &gt;= 0; end of annotated range (exclusive);
+ * <code>Integer.MAX_VALUE</code> if unclosed
+ */
+ private int end;
+
+ /** non-null; annotation text */
+ private final String text;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start &gt;= 0; start of annotated range
+ * @param end &gt;= start; end of annotated range (exclusive) or
+ * <code>Integer.MAX_VALUE</code> if unclosed
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, int end, String text) {
+ this.start = start;
+ this.end = end;
+ this.text = text;
+ }
+
+ /**
+ * Constructs an instance. It is initally unclosed.
+ *
+ * @param start &gt;= 0; start of annotated range
+ * @param text non-null; annotation text
+ */
+ public Annotation(int start, String text) {
+ this(start, Integer.MAX_VALUE, text);
+ }
+
+ /**
+ * Sets the end as given, but only if the instance is unclosed;
+ * otherwise, do nothing.
+ *
+ * @param end &gt;= start; the end
+ */
+ public void setEndIfUnset(int end) {
+ if (this.end == Integer.MAX_VALUE) {
+ this.end = end;
+ }
+ }
+
+ /**
+ * Sets the end as given.
+ *
+ * @param end &gt;= start; the end
+ */
+ public void setEnd(int end) {
+ this.end = end;
+ }
+
+ /**
+ * Gets the start.
+ *
+ * @return the start
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end.
+ *
+ * @return the end
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the text.
+ *
+ * @return non-null; the text
+ */
+ public String getText() {
+ return text;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java b/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java
new file mode 100644
index 0000000..2bfd8cd
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java
@@ -0,0 +1,451 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import org.jf.dexlib.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is intended to provide an easy to use container to build up a method's debug info. You can easily add
+ * an "event" at a specific address, where an event is something like a line number, start/end local, etc.
+ * The events must be added such that the code addresses increase monotonically. This matches how a parser would
+ * generally behave, and is intended to increase performance.
+ */
+public class DebugInfoBuilder
+{
+ private static final int LINE_BASE = -4;
+ private static final int LINE_RANGE = 15;
+ private static final int FIRST_SPECIAL = 0x0a;
+
+ private int lineStart = 0;
+ private ArrayList<String> parameterNames = new ArrayList<String>();
+ private ArrayList<Event> events = new ArrayList<Event>();
+ private int lastAddress = 0;
+
+ private boolean hasData;
+
+ private int currentAddress;
+ private int currentLine;
+
+ public DebugInfoBuilder() {
+ }
+
+ private void checkAddress(int address) {
+ if (lastAddress > address) {
+ throw new RuntimeException("Cannot add an event with an address before the address of the prior event");
+ }
+ }
+
+ public void addParameterName(String parameterName) {
+ if (parameterName != null) {
+ hasData = true;
+ }
+
+ parameterNames.add(parameterName);
+ }
+
+ public void addLine(int address, int line) {
+ hasData = true;
+
+ checkAddress(address);
+
+ if (lineStart == 0) {
+ lineStart = line;
+ }
+
+ events.add(new LineEvent(address, line));
+ }
+
+ public void addLocal(int address, int registerNumber, String localName, String localType) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new StartLocalEvent(address, registerNumber, localName, localType));
+ }
+
+ public void addLocalExtended(int address, int registerNumber, String localName, String localType,
+ String signature) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature));
+ }
+
+ public void addEndLocal(int address, int registerNumber) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new EndLocalEvent(address, registerNumber));
+ }
+
+ public void addRestartLocal(int address, int registerNumber) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new RestartLocalEvent(address, registerNumber));
+ }
+
+ public void addPrologue(int address) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new PrologueEvent(address));
+ }
+
+ public void addEpilogue(int address) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new EpilogueEvent(address));
+ }
+
+ public void addSetFile(int address, String fileName) {
+ hasData = true;
+
+ checkAddress(address);
+
+ events.add(new SetFileEvent(address, fileName));
+ }
+
+ public int getParameterNameCount() {
+ return parameterNames.size();
+ }
+
+ public DebugInfoItem encodeDebugInfo(DexFile dexFile) {
+ if (!hasData) {
+ return null;
+ }
+
+ ByteArrayOutput out = new ByteArrayOutput();
+ StringIdItem[] parameterNamesArray = new StringIdItem[parameterNames.size()];
+ ArrayList<Item> referencedItems = new ArrayList<Item>();
+
+ if (lineStart == 0) {
+ lineStart = 1;
+ }
+
+ currentLine = lineStart;
+
+ for (Event event: events) {
+ event.emit(dexFile, out, referencedItems);
+ }
+ emitEndSequence(out);
+
+ int index = 0;
+ for (String parameterName: parameterNames) {
+ if (parameterName == null) {
+ parameterNamesArray[index++] = null;
+ } else {
+ parameterNamesArray[index++] = StringIdItem.internStringIdItem(dexFile, parameterName);
+ }
+ }
+
+ Item[] referencedItemsArray = new Item[referencedItems.size()];
+ referencedItems.toArray(referencedItemsArray);
+ return DebugInfoItem.internDebugInfoItem(dexFile, lineStart, parameterNamesArray, out.toByteArray(),
+ referencedItemsArray);
+ }
+
+ public static byte calculateSpecialOpcode(int lineDelta, int addressDelta) {
+ return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE));
+ }
+
+ private interface Event
+ {
+ int getAddress();
+ void emit(DexFile dexFile, Output out, List<Item> referencedItems);
+ }
+
+ private void emitEndSequence(Output out) {
+ out.writeByte(0);
+ }
+
+ private void emitAdvancePC(Output out, int address) {
+ int addressDelta = address-currentAddress;
+
+ if (addressDelta > 0) {
+ out.writeByte(1);
+ out.writeUnsignedLeb128(addressDelta);
+ currentAddress = address;
+ }
+ }
+
+ private void emitAdvanceLine(Output out, int lineDelta) {
+ out.writeByte(2);
+ out.writeSignedLeb128(lineDelta);
+ }
+
+ private void emitStartLocal(Output out, int registerNum) {
+ out.writeByte(3);
+ out.writeUnsignedLeb128(registerNum);
+ out.writeByte(1);
+ out.writeByte(1);
+ }
+
+ private void emitStartLocalExtended(Output out, int registerNum) {
+ out.writeByte(4);
+ out.writeUnsignedLeb128(registerNum);
+ out.writeByte(1);
+ out.writeByte(1);
+ out.writeByte(1);
+ }
+
+ private void emitEndLocal(Output out, int registerNum) {
+ out.writeByte(5);
+ out.writeUnsignedLeb128(registerNum);
+ }
+
+ private void emitRestartLocal(Output out, int registerNum) {
+ out.writeByte(6);
+ out.writeUnsignedLeb128(registerNum);
+ }
+
+ private void emitSetPrologueEnd(Output out) {
+ out.writeByte(7);
+ }
+
+ private void emitSetEpilogueBegin(Output out) {
+ out.writeByte(8);
+ }
+
+ private void emitSetFile(Output out) {
+ out.writeByte(9);
+ out.writeByte(1);
+ }
+
+ private void emitSpecialOpcode(Output out, byte opcode) {
+ out.writeByte(opcode);
+ }
+
+ private class LineEvent implements Event
+ {
+ private final int address;
+ private final int line;
+
+ public LineEvent(int address, int line) {
+ this.address = address;
+ this.line = line;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ int lineDelta = line - currentLine;
+ int addressDelta = address - currentAddress;
+
+ if (lineDelta < -4 || lineDelta > 10) {
+ emitAdvanceLine(out, lineDelta);
+ lineDelta = 0;
+ }
+ if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) {
+ emitAdvancePC(out, address);
+ addressDelta = 0;
+ }
+
+ //TODO: need to handle the case when the line delta is larger than a signed int
+ emitSpecialOpcode(out, calculateSpecialOpcode(lineDelta, addressDelta));
+
+ currentAddress = address;
+ currentLine = line;
+ }
+ }
+
+ private class StartLocalEvent implements Event
+ {
+ private final int address;
+ private final int registerNum;
+ private final String localName;
+ private final String localType;
+
+ public StartLocalEvent(int address, int registerNum, String localName, String localType) {
+ this.address = address;
+ this.registerNum = registerNum;
+ this.localName = localName;
+ this.localType = localType;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitStartLocal(out, registerNum);
+ referencedItems.add(localName==null?null:StringIdItem.internStringIdItem(dexFile, localName));
+ referencedItems.add(localType==null?null:TypeIdItem.internTypeIdItem(dexFile,
+ StringIdItem.internStringIdItem(dexFile, localType)));
+ }
+ }
+
+ private class StartLocalExtendedEvent implements Event
+ {
+ private final int address;
+ private final int registerNum;
+ private final String localName;
+ private final String localType;
+ private final String signature;
+
+ public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType,
+ String signature) {
+ this.address = address;
+ this.registerNum = registerNum;
+ this.localName = localName;
+ this.localType = localType;
+ this.signature = signature;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitStartLocalExtended(out, registerNum);
+ if (localName != null) {
+ referencedItems.add(StringIdItem.internStringIdItem(dexFile, localName));
+ }
+ if (localType != null) {
+ referencedItems.add(TypeIdItem.internTypeIdItem(dexFile,
+ StringIdItem.internStringIdItem(dexFile, localType)));
+ }
+ if (signature != null) {
+ referencedItems.add(StringIdItem.internStringIdItem(dexFile, signature));
+ }
+ }
+ }
+
+ private class EndLocalEvent implements Event
+ {
+ private final int address;
+ private final int registerNum;
+
+ public EndLocalEvent(int address, int registerNum) {
+ this.address = address;
+ this.registerNum = registerNum;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitEndLocal(out, registerNum);
+ }
+ }
+
+ private class RestartLocalEvent implements Event
+ {
+ private final int address;
+ private final int registerNum;
+
+ public RestartLocalEvent(int address, int registerNum) {
+ this.address = address;
+ this.registerNum = registerNum;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitRestartLocal(out, registerNum);
+ }
+ }
+
+ private class PrologueEvent implements Event
+ {
+ private final int address;
+
+ public PrologueEvent(int address) {
+ this.address = address;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitSetPrologueEnd(out);
+ }
+ }
+
+ private class EpilogueEvent implements Event
+ {
+ private final int address;
+
+ public EpilogueEvent(int address) {
+ this.address = address;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitSetEpilogueBegin(out);
+ }
+ }
+
+ private class SetFileEvent implements Event
+ {
+ private final int address;
+ private final String fileName;
+
+ public SetFileEvent(int address, String fileName) {
+ this.address = address;
+ this.fileName = fileName;
+ }
+
+ public int getAddress() {
+ return address;
+ }
+
+ public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
+ emitAdvancePC(out, address);
+ emitSetFile(out);
+ if (fileName != null) {
+ referencedItems.add(StringIdItem.internStringIdItem(dexFile, fileName));
+ }
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java
new file mode 100644
index 0000000..91f1407
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java
@@ -0,0 +1,143 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public class EncodedValueUtils {
+ public static byte getRequiredBytesForSignedIntegralValue(long value) {
+ /*
+ * Figure out how many bits are needed to represent the value,
+ * including a sign bit: The bit count is subtracted from 65
+ * and not 64 to account for the sign bit. The xor operation
+ * has the effect of leaving non-negative values alone and
+ * unary complementing negative values (so that a leading zero
+ * count always returns a useful number for our present
+ * purpose).
+ */
+ int requiredBits =
+ 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+ // Round up the requiredBits to a number of bytes.
+ return (byte)((requiredBits + 0x07) >> 3);
+ }
+
+ public static long decodeSignedIntegralValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << (i * 8));
+ }
+
+ int shift = (8 - bytes.length) * 8;
+ return value << shift >> shift;
+ }
+
+ public static byte[] encodeSignedIntegralValue(long value) {
+ int requiredBytes = getRequiredBytesForSignedIntegralValue(value);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for (int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte) value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+
+
+
+
+
+ public static byte getRequiredBytesForUnsignedIntegralValue(long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ return (byte)((requiredBits + 0x07) >> 3);
+ }
+
+ public static long decodeUnsignedIntegralValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << i * 8);
+ }
+ return value;
+ }
+
+ public static byte[] encodeUnsignedIntegralValue(long value) {
+ int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for (int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte) value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+
+
+
+
+
+ public static int getRequiredBytesForRightZeroExtendedValue(long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ return (requiredBits + 0x07) >> 3;
+ }
+
+ public static long decodeRightZeroExtendedValue(byte[] bytes) {
+ long value = 0;
+ for (int i = 0; i < bytes.length; i++) {
+ value |= (((long)(bytes[i] & 0xFF)) << (i * 8));
+ }
+ return value << (8 - bytes.length) * 8;
+ }
+
+ public static byte[] encodeRightZeroExtendedValue(long value) {
+ int requiredBytes = getRequiredBytesForRightZeroExtendedValue(value);
+
+ // Scootch the first bits to be written down to the low-order bits.
+ value >>= 64 - (requiredBytes * 8);
+
+ byte[] bytes = new byte[requiredBytes];
+
+ for(int i = 0; i < requiredBytes; i++) {
+ bytes[i] = (byte)value;
+ value >>= 8;
+ }
+ return bytes;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java b/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java
new file mode 100644
index 0000000..bc10edf
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext
+ extends RuntimeException {
+ /** non-null; human-oriented context of the exception */
+ private StringBuffer context;
+
+ /**
+ * Augments the given exception with the given context, and return the
+ * result. The result is either the given exception if it was an
+ * {@link ExceptionWithContext}, or a newly-constructed exception if it
+ * was not.
+ *
+ * @param ex non-null; the exception to augment
+ * @param str non-null; context to add
+ * @return non-null; an appropriate instance
+ */
+ public static ExceptionWithContext withContext(Throwable ex, String str) {
+ ExceptionWithContext ewc;
+
+ if (ex instanceof ExceptionWithContext) {
+ ewc = (ExceptionWithContext) ex;
+ } else {
+ ewc = new ExceptionWithContext(ex);
+ }
+
+ ewc.addContext(str);
+ return ewc;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ */
+ public ExceptionWithContext(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cause null-ok; exception that caused this one
+ */
+ public ExceptionWithContext(Throwable cause) {
+ this(null, cause);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ * @param cause null-ok; exception that caused this one
+ */
+ public ExceptionWithContext(String message, Throwable cause) {
+ super((message != null) ? message :
+ (cause != null) ? cause.getMessage() : null,
+ cause);
+
+ if (cause instanceof ExceptionWithContext) {
+ String ctx = ((ExceptionWithContext) cause).context.toString();
+ context = new StringBuffer(ctx.length() + 200);
+ context.append(ctx);
+ } else {
+ context = new StringBuffer(200);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintStream out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintWriter out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /**
+ * Adds a line of context to this instance.
+ *
+ * @param str non-null; new context
+ */
+ public void addContext(String str) {
+ if (str == null) {
+ throw new NullPointerException("str == null");
+ }
+
+ context.append(str);
+ if (!str.endsWith("\n")) {
+ context.append('\n');
+ }
+ }
+
+ /**
+ * Gets the context.
+ *
+ * @return non-null; the context
+ */
+ public String getContext() {
+ return context.toString();
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out non-null; where to print to
+ */
+ public void printContext(PrintStream out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out non-null; where to print to
+ */
+ public void printContext(PrintWriter out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java
new file mode 100644
index 0000000..d8cd9bc
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private FileUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Reads the named file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param fileName non-null; name of the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(String fileName)
+ throws IOException {
+ File file = new File(fileName);
+ return readFile(file);
+ }
+
+ /**
+ * Reads the given file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param file non-null; the file to read
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(File file)
+ throws IOException {
+ return readFile(file, 0, -1);
+ }
+
+ /**
+ * Reads the specified block from the given file, translating
+ * {@link IOException} to a {@link RuntimeException} of some sort.
+ *
+ * @param file non-null; the file to read
+ * @param offset the offset to begin reading
+ * @param length the number of bytes to read, or -1 to read to the
+ * end of the file
+ * @return non-null; contents of the file
+ */
+ public static byte[] readFile(File file, int offset, int length)
+ throws IOException {
+ if (!file.exists()) {
+ throw new RuntimeException(file + ": file not found");
+ }
+
+ if (!file.isFile()) {
+ throw new RuntimeException(file + ": not a file");
+ }
+
+ if (!file.canRead()) {
+ throw new RuntimeException(file + ": file not readable");
+ }
+
+ long longLength = file.length();
+ int fileLength = (int) longLength;
+ if (fileLength != longLength) {
+ throw new RuntimeException(file + ": file too long");
+ }
+
+ if (length == -1) {
+ length = fileLength - offset;
+ }
+
+ if (offset + length > fileLength) {
+ throw new RuntimeException(file + ": file too short");
+ }
+
+ FileInputStream in = new FileInputStream(file);
+
+ int at = offset;
+ while(at > 0) {
+ long amt = in.skip(at);
+ if (amt == -1) {
+ throw new RuntimeException(file + ": unexpected EOF");
+ }
+ at -= amt;
+ }
+
+ byte[] result = readStream(in, length);
+
+ in.close();
+
+ return result;
+ }
+
+ public static byte[] readStream(InputStream in, int length)
+ throws IOException {
+ byte[] result = new byte[length];
+ int at=0;
+
+ while (length > 0) {
+ int amt = in.read(result, at, length);
+ if (amt == -1) {
+ throw new RuntimeException("unexpected EOF");
+ }
+ at += amt;
+ length -= amt;
+ }
+
+ return result;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java b/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java
new file mode 100644
index 0000000..a56bf5f
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java
@@ -0,0 +1,315 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Utilities for formatting numbers as hexadecimal.
+ */
+public final class Hex {
+ /**
+ * This class is uninstantiable.
+ */
+ private Hex() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Formats a <code>long</code> as an 8-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u8(long v) {
+ char[] result = new char[16];
+ for (int i = 0; i < 16; i++) {
+ result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 4-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u4(int v) {
+ char[] result = new char[8];
+ for (int i = 0; i < 8; i++) {
+ result[7 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 3-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u3(int v) {
+ char[] result = new char[6];
+ for (int i = 0; i < 6; i++) {
+ result[5 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 2-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u2(int v) {
+ char[] result = new char[4];
+ for (int i = 0; i < 4; i++) {
+ result[3 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as either a 2-byte unsigned hex value
+ * (if the value is small enough) or a 4-byte unsigned hex value (if
+ * not).
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u2or4(int v) {
+ if (v == (char) v) {
+ return u2(v);
+ } else {
+ return u4(v);
+ }
+ }
+
+ /**
+ * Formats an <code>int</code> as a 1-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String u1(int v) {
+ char[] result = new char[2];
+ for (int i = 0; i < 2; i++) {
+ result[1 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 4-bit unsigned hex nibble.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String uNibble(int v) {
+ char[] result = new char[1];
+
+ result[0] = Character.forDigit(v & 0x0f, 16);
+ return new String(result);
+ }
+
+ /**
+ * Formats a <code>long</code> as an 8-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s8(long v) {
+ char[] result = new char[17];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 16; i++) {
+ result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 4-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s4(int v) {
+ char[] result = new char[9];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 8; i++) {
+ result[8 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 2-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s2(int v) {
+ char[] result = new char[5];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 4; i++) {
+ result[4 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an <code>int</code> as a 1-byte signed hex value.
+ *
+ * @param v value to format
+ * @return non-null; formatted form
+ */
+ public static String s1(int v) {
+ char[] result = new char[3];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 2; i++) {
+ result[2 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats a hex dump of a portion of a <code>byte[]</code>. The result
+ * is always newline-terminated, unless the passed-in length was zero,
+ * in which case the result is always the empty string (<code>""</code>).
+ *
+ * @param arr non-null; array to format
+ * @param offset &gt;= 0; offset to the part to dump
+ * @param length &gt;= 0; number of bytes to dump
+ * @param outOffset &gt;= 0; first output offset to print
+ * @param bpl &gt;= 0; number of bytes of output per line
+ * @param addressLength {2,4,6,8}; number of characters for each address
+ * header
+ * @return non-null; a string of the dump
+ */
+ public static String dump(byte[] arr, int offset, int length,
+ int outOffset, int bpl, int addressLength) {
+ int end = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (end > arr.length)) {
+ throw new IndexOutOfBoundsException("arr.length " +
+ arr.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (outOffset < 0) {
+ throw new IllegalArgumentException("outOffset < 0");
+ }
+
+ if (length == 0) {
+ return "";
+ }
+
+ StringBuffer sb = new StringBuffer(length * 4 + 6);
+ boolean bol = true;
+ int col = 0;
+
+ while (length > 0) {
+ if (col == 0) {
+ String astr;
+ switch (addressLength) {
+ case 2: astr = Hex.u1(outOffset); break;
+ case 4: astr = Hex.u2(outOffset); break;
+ case 6: astr = Hex.u3(outOffset); break;
+ default: astr = Hex.u4(outOffset); break;
+ }
+ sb.append(astr);
+ sb.append(": ");
+ } else if ((col & 1) == 0) {
+ sb.append(' ');
+ }
+ sb.append(Hex.u1(arr[offset]));
+ outOffset++;
+ offset++;
+ col++;
+ if (col == bpl) {
+ sb.append('\n');
+ col = 0;
+ }
+ length--;
+ }
+
+ if (col != 0) {
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java b/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java
new file mode 100644
index 0000000..e8b85e0
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that wraps another writer and passes width-limited and
+ * optionally-prefixed output to its subordinate. When lines are
+ * wrapped they are automatically indented based on the start of the
+ * line.
+ */
+public final class IndentingWriter extends FilterWriter {
+ /** null-ok; optional prefix for every line */
+ private final String prefix;
+
+ /** &gt; 0; the maximum output width */
+ private final int width;
+
+ /** &gt; 0; the maximum indent */
+ private final int maxIndent;
+
+ /** &gt;= 0; current output column (zero-based) */
+ private int column;
+
+ /** whether indent spaces are currently being collected */
+ private boolean collectingIndent;
+
+ /** &gt;= 0; current indent amount */
+ private int indent;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param width &gt;= 0; the maximum output width (not including
+ * <code>prefix</code>), or <code>0</code> for no maximum
+ * @param prefix non-null; the prefix for each line
+ */
+ public IndentingWriter(Writer out, int width, String prefix) {
+ super(out);
+
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (width < 0) {
+ throw new IllegalArgumentException("width < 0");
+ }
+
+ if (prefix == null) {
+ throw new NullPointerException("prefix == null");
+ }
+
+ this.width = (width != 0) ? width : Integer.MAX_VALUE;
+ this.maxIndent = width >> 1;
+ this.prefix = (prefix.length() == 0) ? null : prefix;
+
+ bol();
+ }
+
+ /**
+ * Constructs a no-prefix instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param width &gt;= 0; the maximum output width (not including
+ * <code>prefix</code>), or <code>0</code> for no maximum
+ */
+ public IndentingWriter(Writer out, int width) {
+ this(out, width, "");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(int c) throws IOException {
+ synchronized (lock) {
+ if (collectingIndent) {
+ if (c == ' ') {
+ indent++;
+ if (indent >= maxIndent) {
+ indent = maxIndent;
+ collectingIndent = false;
+ }
+ } else {
+ collectingIndent = false;
+ }
+ }
+
+ if ((column == width) && (c != '\n')) {
+ out.write('\n');
+ column = 0;
+ /*
+ * Note: No else, so this should fall through to the next
+ * if statement.
+ */
+ }
+
+ if (column == 0) {
+ if (prefix != null) {
+ out.write(prefix);
+ }
+
+ if (!collectingIndent) {
+ for (int i = 0; i < indent; i++) {
+ out.write(' ');
+ }
+ column = indent;
+ }
+ }
+
+ out.write(c);
+
+ if (c == '\n') {
+ bol();
+ } else {
+ column++;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(cbuf[off]);
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(str.charAt(off));
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /**
+ * Indicates that output is at the beginning of a line.
+ */
+ private void bol() {
+ column = 0;
+ collectingIndent = (maxIndent != 0);
+ indent = 0;
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Input.java b/dexlib/src/main/java/org/jf/dexlib/Util/Input.java
new file mode 100644
index 0000000..2364fab
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Input.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Interface for a source for binary input. This is similar to
+ * <code>java.util.DataInput</code>, but no <code>IOExceptions</code>
+ * are declared, and multibyte input is defined to be little-endian.
+ */
+public interface Input {
+ /**
+ * Gets the current cursor position. This is the same as the number of
+ * bytes read from this instance.
+ *
+ * @return &gt;= 0; the cursor position
+ */
+ public int getCursor();
+
+ /**
+ * Sets the current cursor position.
+ *
+ * @return &gt;= 0; the cursor position
+ */
+ public void setCursor(int cursor);
+
+ /**
+ * Asserts that the cursor is the given value.
+ *
+ * @param expectedCursor the expected cursor value
+ * @throws RuntimeException thrown if <code>getCursor() !=
+ * expectedCursor</code>
+ */
+ public void assertCursor(int expectedCursor);
+
+ /**
+ * Reads a <code>byte</code> from this instance.
+ *
+ * @return the byte value that was read
+ */
+ public byte readByte();
+
+ /**
+ * Reads a <code>short</code> from this instance.
+ *
+ * @return the short value that was read, as an int
+ */
+ public int readShort();
+
+ /**
+ * Reads an <code>int</code> from this instance.
+ *
+ * @return the unsigned int value that was read
+ */
+ public int readInt();
+
+ /**
+ * Reads a <code>long</code> from this instance.
+ *
+ * @return the long value that was read
+ */
+ public long readLong();
+
+
+ /**
+ * Reads a DWARFv3-style signed LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @return the integer value that was read
+ */
+ public int readSignedLeb128();
+
+ /**
+ * Reads a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @return the integer value that was read
+ */
+ public int readUnsignedLeb128();
+
+
+ /**
+ * Reads a unsigned value as a DWARFv3-style LEB128 integer. It specifically
+ * checks for the case when the value was incorrectly formatted as a signed
+ * LEB128, and returns the appropriate unsigned value, but negated
+ * @return If the value was formatted as a ULEB128, it returns the actual unsigned
+ * value. Otherwise, if the value was formatted as a signed LEB128, it negates the
+ * "correct" unsigned value and returns that
+ */
+ public int readUnsignedOrSignedLeb128();
+
+ /**
+ * reads a <code>byte[]</code> from this instance.
+ *
+ * @param bytes non-null; the buffer to read the data into
+ * @param offset &gt;= 0; offset into <code>bytes</code> for the first
+ * byte to write
+ * @param length &gt;= 0; number of bytes to read
+ */
+ public void read(byte[] bytes, int offset, int length);
+
+ /**
+ * reads a <code>byte[]</code> from this instance. This is just
+ * a convenient shorthand for <code>read(bytes, 0, bytes.length)</code>.
+ *
+ * @param bytes non-null; the buffer to read the data into
+ */
+ public void read(byte[] bytes);
+
+
+ /**
+ * reads a <code>byte[]</code> from this instance
+ *
+ * @param length &gt;= 0; number of bytes to read
+ * @return a byte array containing <code>length</code> bytes
+ */
+ public byte[] readBytes(int length);
+
+ /**
+ * reads and decodes a null terminated utf8 string from the current cursor up to but not including
+ * the next null (0) byte. The terminating null byte is read and discarded, so that after the read,
+ * the cursor is positioned at the byte immediately after the terminating null
+ *
+ * @return a string representing the decoded value
+ */
+ public String realNullTerminatedUtf8String();
+
+ /**
+ * Skips the given number of bytes.
+ *
+ * @param count &gt;= 0; the number of bytes to skip
+ */
+ public void skipBytes(int count);
+
+ /**
+ * Skip extra bytes if necessary to force alignment of the output
+ * cursor as given.
+ *
+ * @param alignment &gt; 0; the alignment; must be a power of two
+ */
+ public void alignTo(int alignment);
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java
new file mode 100644
index 0000000..a5aafe6
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * LEB128 (little-endian base 128) utilities.
+ */
+public final class Leb128Utils {
+ /**
+ * This class is uninstantiable.
+ */
+ private Leb128Utils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the number of bytes in the unsigned LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int unsignedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >>> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ value = remaining;
+ remaining >>>= 7;
+ count++;
+ }
+
+ return count + 1;
+ }
+
+ /**
+ * Gets the number of bytes in the signed LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int signedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /**
+ * Writes an unsigned leb128 to the buffer at the specified location
+ * @param value the value to write as an unsigned leb128
+ * @param buffer the buffer to write to
+ * @param bufferIndex the index to start writing at
+ */
+ public static void writeUnsignedLeb128(int value, byte[] buffer, int bufferIndex) {
+ int remaining = value >>> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ buffer[bufferIndex] = (byte)((value & 0x7f) | 0x80);
+ bufferIndex++;
+ value = remaining;
+ remaining >>>= 7;
+ count++;
+ }
+
+ buffer[bufferIndex] = (byte)(value & 0x7f);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java
new file mode 100644
index 0000000..2e97f51
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java
@@ -0,0 +1,200 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public class NumberUtils {
+
+ /**
+ * Decodes the high signed 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded signed nibble
+ */
+ public static byte decodeHighSignedNibble(byte b) {
+ return (byte)(b >> 4);
+ }
+
+ /**
+ * Decodes the low signed 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded signed nibble
+ */
+ public static byte decodeLowSignedNibble(byte b) {
+ return (byte)(((byte)(b << 4)) >> 4);
+ }
+
+ /**
+ * Decodes the high unsigned 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded unsigned nibble
+ */
+ public static byte decodeHighUnsignedNibble(byte b) {
+ return (byte)((b & 0xFF) >>> 4);
+ }
+
+ /**
+ * Decodes the low unsigned 4-bit nibble from the given byte
+ * @param b the byte to decode
+ * @return the decoded unsigned nibble
+ */
+ public static byte decodeLowUnsignedNibble(byte b) {
+ return (byte)(b & 0x0F);
+ }
+
+ /**
+ * Decodes an unsigned byte from a signed byte
+ * @param b the signed byte to decode
+ * @return the decoded unsigned byte as a short
+ */
+ public static short decodeUnsignedByte(byte b) {
+ return (short)(b & 0xFF);
+ }
+
+ /**
+ * Decodes a signed short value from 2 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param msb the most significant byte
+ * @return the decoded signed short value
+ */
+ public static short decodeShort(byte lsb, byte msb) {
+ return (short)
+ ( (lsb & 0xFF) |
+ (msb << 8)
+ );
+ }
+
+ /**
+ * Decodes a signed short value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed short value to decode
+ * @return the decoded signed short value
+ */
+ public static short decodeShort(byte[] bytes, int index) {
+ return (short)
+ ( (bytes[index++] & 0xFF) |
+ (bytes[index] << 8)
+ );
+ }
+
+ /**
+ * Decodes an unsigned short value from 2 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param msb the most significant byte
+ * @return the decoded unsigned short value as an int
+ */
+ public static int decodeUnsignedShort(byte lsb, byte msb) {
+ return ( (lsb & 0xFF) |
+ ((msb & 0xFF) << 8)
+ );
+ }
+
+ /**
+ * Decodes an unsigned short value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the unsigned short value to decode
+ * @return the decoded unsigned short value as an int
+ */
+ public static int decodeUnsignedShort(byte[] bytes, int index) {
+ return ( (bytes[index++] & 0xFF) |
+ ((bytes[index] & 0xFF) << 8)
+ );
+ }
+
+ /**
+ * Decodes a signed integer value from 4 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param lsb the least significant byte
+ * @param mlsb the middle least significant byte
+ * @param mmsb the middle most significant byte
+ * @param msb the most significant byte
+ * @return the decoded signed integer value
+ */
+ public static int decodeInt(byte lsb, byte mlsb, byte mmsb, byte msb) {
+ return (lsb & 0xFF) |
+ ((mlsb & 0xFF) << 8) |
+ ((mmsb & 0xFF) << 16) |
+ (msb << 24);
+ }
+
+ /**
+ * Decodes a signed integer value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed integer value to decode
+ * @return the decoded signed integer value
+ */
+ public static int decodeInt(byte[] bytes, int index) {
+ return (bytes[index++] & 0xFF) |
+ ((bytes[index++] & 0xFF) << 8) |
+ ((bytes[index++] & 0xFF) << 16) |
+ (bytes[index] << 24);
+ }
+
+ /**
+ * Decodes a signed long value from 8 individual bytes
+ * The parameters are in order from least significant byte to most significant byte
+ * @param llsb the lower least significant byte
+ * @param lmlsb the lower middle least significant byte
+ * @param lmmsb the lower middle most significant byte
+ * @param lgsb the lower greater significant byte
+ * @param glsb the greater least significant byte
+ * @param gmlsb the greater middle least significant byte
+ * @param gmmsb the greater middle most significant byte
+ * @param gmsb the greater most significant byte
+ * @return the decoded signed long value
+ */
+ public static long decodeLong(byte llsb, byte lmlsb, byte lmmsb, byte lgsb, byte glsb, byte gmlsb, byte gmmsb,
+ byte gmsb) {
+ return (llsb & 0xFFL) |
+ ((lmlsb & 0xFFL) << 8) |
+ ((lmmsb & 0xFFL) << 16) |
+ ((lgsb & 0xFFL) << 24) |
+ ((glsb & 0xFFL) << 32) |
+ ((gmlsb & 0xFFL) << 40) |
+ ((gmmsb & 0xFFL) << 48) |
+ (((long)gmsb) << 56);
+ }
+
+ /**
+ * Decodes a signed long value in little endian format from the given byte array at the given index.
+ * @param bytes the byte array
+ * @param index the index of the first byte of the signed long value to decode
+ * @return the decoded signed long value
+ */
+ public static long decodeLong(byte[] bytes, int index) {
+ return (bytes[index++] & 0xFFL) |
+ ((bytes[index++] & 0xFFL) << 8) |
+ ((bytes[index++] & 0xFFL) << 16) |
+ ((bytes[index++] & 0xFFL) << 24) |
+ ((bytes[index++] & 0xFFL) << 32) |
+ ((bytes[index++] & 0xFFL) << 40) |
+ ((bytes[index++] & 0xFFL) << 48) |
+ (((long)bytes[index]) << 56);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Output.java b/dexlib/src/main/java/org/jf/dexlib/Util/Output.java
new file mode 100644
index 0000000..49b4133
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Output.java
@@ -0,0 +1,141 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * Interface for a sink for binary output. This is similar to
+ * <code>java.util.DataOutput</code>, but no <code>IOExceptions</code>
+ * are declared, and multibyte output is defined to be little-endian.
+ */
+public interface Output {
+ /**
+ * Gets the current cursor position. This is the same as the number of
+ * bytes written to this instance.
+ *
+ * @return &gt;= 0; the cursor position
+ */
+ public int getCursor();
+
+ /**
+ * Asserts that the cursor is the given value.
+ *
+ * @param expectedCursor the expected cursor value
+ * @throws RuntimeException thrown if <code>getCursor() !=
+ * expectedCursor</code>
+ */
+ public void assertCursor(int expectedCursor);
+
+ /**
+ * Writes a <code>byte</code> to this instance.
+ *
+ * @param value the value to write; all but the low 8 bits are ignored
+ */
+ public void writeByte(int value);
+
+ /**
+ * Writes a <code>short</code> to this instance.
+ *
+ * @param value the value to write; all but the low 16 bits are ignored
+ */
+ public void writeShort(int value);
+
+ /**
+ * Writes an <code>int</code> to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeInt(int value);
+
+ /**
+ * Writes a <code>long</code> to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeLong(long value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write, treated as an unsigned value
+ * @return 1..5; the number of bytes actually written
+ */
+ public int writeUnsignedLeb128(int value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write
+ * @return 1..5; the number of bytes actually written
+ */
+ public int writeSignedLeb128(int value);
+
+ /**
+ * Writes a {@link org.jf.dexlib.Util.ByteArray} to this instance.
+ *
+ * @param bytes non-null; the array to write
+ */
+ public void write(ByteArray bytes);
+
+ /**
+ * Writes a portion of a <code>byte[]</code> to this instance.
+ *
+ * @param bytes non-null; the array to write
+ * @param offset &gt;= 0; offset into <code>bytes</code> for the first
+ * byte to write
+ * @param length &gt;= 0; number of bytes to write
+ */
+ public void write(byte[] bytes, int offset, int length);
+
+ /**
+ * Writes a <code>byte[]</code> to this instance. This is just
+ * a convenient shorthand for <code>write(bytes, 0, bytes.length)</code>.
+ *
+ * @param bytes non-null; the array to write
+ */
+ public void write(byte[] bytes);
+
+ /**
+ * Writes the given number of <code>0</code> bytes.
+ *
+ * @param count &gt;= 0; the number of zeroes to write
+ */
+ public void writeZeroes(int count);
+
+ /**
+ * Adds extra bytes if necessary (with value <code>0</code>) to
+ * force alignment of the output cursor as given.
+ *
+ * @param alignment &gt; 0; the alignment; must be a power of two
+ */
+ public void alignTo(int alignment);
+} \ No newline at end of file
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java b/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java
new file mode 100644
index 0000000..f246c99
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java
@@ -0,0 +1,40 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+public class Pair<A, B> {
+ public final A first;
+ public final B second;
+
+ public Pair(A first, B second) {
+ this.first = first;
+ this.second = second;
+ }
+}
+
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java b/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java
new file mode 100644
index 0000000..2667979
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java
@@ -0,0 +1,52 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import java.util.AbstractList;
+import java.util.RandomAccess;
+
+public class ReadOnlyArrayList<T> extends AbstractList<T> implements RandomAccess {
+ private final T[] arr;
+
+ public ReadOnlyArrayList(T[] arr) {
+ this.arr = arr;
+ }
+
+ public int size() {
+ return arr.length;
+ }
+
+ public T get(int i) {
+ return arr[i];
+ }
+
+ public static <T> ReadOnlyArrayList<T> of(T... items) {
+ return new ReadOnlyArrayList<T>(items);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java b/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java
new file mode 100644
index 0000000..25fb7b4
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * SparseArrays map integers to Objects. Unlike a normal array of Objects,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Integers to Objects.
+ */
+public class SparseArray<E> {
+ private static final Object DELETED = new Object();
+ private boolean mGarbage = false;
+
+ /**
+ * Creates a new SparseArray containing no mappings.
+ */
+ public SparseArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public SparseArray(int initialCapacity) {
+ mKeys = new int[initialCapacity];
+ mValues = new Object[initialCapacity];
+ mSize = 0;
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or <code>null</code>
+ * if no such mapping has been made.
+ */
+ public E get(int key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(int key, E valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
+ /**
+ * Alias for {@link #delete(int)}.
+ */
+ public void remove(int key) {
+ delete(key);
+ }
+
+ private void gc() {
+ // Log.e("SparseArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ int[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, E value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~binarySearch(mKeys, 0, mSize, key);
+ }
+
+ if (mSize >= mKeys.length) {
+ int n = Math.max(mSize + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public int keyAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public E valueAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return (E) mValues[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, sets a new
+ * value for the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = Math.max(pos + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ /**
+ * Increases the size of the underlying storage if needed, to ensure that it can
+ * hold the specified number of items without having to allocate additional memory
+ * @param capacity the number of items
+ */
+ public void ensureCapacity(int capacity) {
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ if (mKeys.length < capacity) {
+ int[] nkeys = new int[capacity];
+ Object[] nvalues = new Object[capacity];
+
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+ }
+
+ private static int binarySearch(int[] a, int start, int len, int key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ /**
+ * @return a read-only list of the values in this SparseArray which are in ascending order, based on their
+ * associated key
+ */
+ public List<E> getValues() {
+ return Collections.unmodifiableList(Arrays.asList((E[])mValues));
+ }
+
+ private int[] mKeys;
+ private Object[] mValues;
+ private int mSize;
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java b/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java
new file mode 100644
index 0000000..4e687f1
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+/**
+ * SparseIntArrays map integers to integers. Unlike a normal array of integers,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Integers to Integers.
+ */
+public class SparseIntArray {
+ /**
+ * Creates a new SparseIntArray containing no mappings.
+ */
+ public SparseIntArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseIntArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public SparseIntArray(int initialCapacity) {
+ mKeys = new int[initialCapacity];
+ mValues = new int[initialCapacity];
+ mSize = 0;
+ }
+
+ /**
+ * Gets the int mapped from the specified key, or <code>0</code>
+ * if no such mapping has been made.
+ */
+ public int get(int key) {
+ return get(key, 0);
+ }
+
+ /**
+ * Gets the int mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public int get(int key, int valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0) {
+ return valueIfKeyNotFound;
+ } else {
+ return mValues[i];
+ }
+ }
+
+ /**
+ * Gets the int mapped from the specified key, or if not present, the
+ * closest key that is less than the specified key.
+ */
+ public int getClosestSmaller(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0) {
+ i = ~i;
+ if (i > 0) {
+ i--;
+ }
+ return mValues[i];
+ } else {
+ return mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ removeAt(i);
+ }
+ }
+
+ /**
+ * Removes the mapping at the given index.
+ */
+ public void removeAt(int index) {
+ System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+ mSize--;
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, int value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (mSize >= mKeys.length) {
+ int n = Math.max(mSize + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ int[] nvalues = new int[n];
+
+ // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseIntArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseIntArray
+ * currently stores.
+ */
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseIntArray stores.
+ */
+ public int keyAt(int index) {
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseIntArray stores.
+ */
+ public int valueAt(int index) {
+ return mValues[index];
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(int value) {
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseIntArray.
+ */
+ public void clear() {
+ mSize = 0;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, int value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = Math.max(pos + 1, mKeys.length * 2);
+
+ int[] nkeys = new int[n];
+ int[] nvalues = new int[n];
+
+ // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ private static int binarySearch(int[] a, int start, int len, int key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ private int[] mKeys;
+ private int[] mValues;
+ private int mSize;
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java b/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java
new file mode 100644
index 0000000..aadcaa7
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java
@@ -0,0 +1,347 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import org.jf.dexlib.CodeItem;
+import org.jf.dexlib.TypeIdItem;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+public class TryListBuilder
+{
+ /*TODO: add logic to merge adjacent, identical try blocks, and remove superflous handlers
+ Also provide a "strict" mode, where the above isn't performed, which will be useful to be able to
+ exactly reproduce the original .dex file (for testing/verification purposes)*/
+
+
+ private TryRange firstTryRange = new TryRange(0,0);
+ private TryRange lastTryRange = new TryRange(0,0);
+
+ public TryListBuilder() {
+ firstTryRange.next = lastTryRange;
+ lastTryRange.previous = firstTryRange;
+ }
+
+ private class TryRange
+ {
+ public TryRange previous = null;
+ public TryRange next = null;
+
+ public int startAddress;
+ public int endAddress;
+ public LinkedList<Handler> handlers;
+
+ public int catchAllHandlerAddress;
+
+ public TryRange(int startAddress, int endAddress) {
+ this.startAddress = startAddress;
+ this.endAddress = endAddress;
+ this.handlers = new LinkedList<Handler>();
+ this.previous = null;
+ this.next = null;
+ catchAllHandlerAddress = -1;
+ }
+
+ public void append(TryRange tryRange) {
+ /*we use a dummy last item, so this.next will always
+ have a value*/
+ this.next.previous = tryRange;
+ tryRange.next = this.next;
+
+ this.next = tryRange;
+ tryRange.previous = this;
+ }
+
+ public void prepend(TryRange tryRange){
+ /*we use a dummy first item, so this.previous will always
+ have a value*/
+ this.previous.next = tryRange;
+ tryRange.previous = this.previous;
+
+ this.previous = tryRange;
+ tryRange.next = this;
+ }
+
+ /**
+ * This splits the current range into two ranges at the given
+ * address. The existing range will be shortened to the first
+ * half, and a new range will be created and returned for the
+ * 2nd half.
+ * @param address The address to split at
+ * @return The 2nd half of the
+ */
+ public TryRange split(int address) {
+ //this is a private class, so address is assumed
+ //to be valid
+
+ TryRange tryRange = new TryRange(address, endAddress);
+ tryRange.catchAllHandlerAddress = this.catchAllHandlerAddress;
+ tryRange.handlers.addAll(this.handlers);
+ append(tryRange);
+
+ this.endAddress = address;
+
+ return tryRange;
+ }
+
+ public void appendHandler(Handler handler) {
+ handlers.addLast(handler);
+ }
+
+ public void prependHandler(Handler handler) {
+ handlers.addFirst(handler);
+ }
+ }
+
+ private class Handler
+ {
+ public final TypeIdItem type;
+ public final int handlerAddress;
+
+ public Handler(TypeIdItem type, int handlerAddress) {
+ this.type = type;
+ this.handlerAddress = handlerAddress;
+ }
+ }
+
+ public Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>> encodeTries() {
+ if (firstTryRange.next == lastTryRange) {
+ return new Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>>(null, null);
+ }
+
+ ArrayList<CodeItem.TryItem> tries = new ArrayList<CodeItem.TryItem>();
+ ArrayList<CodeItem.EncodedCatchHandler> handlers = new ArrayList<CodeItem.EncodedCatchHandler>();
+
+ HashMap<CodeItem.EncodedCatchHandler, CodeItem.EncodedCatchHandler> handlerDict =
+ new HashMap<CodeItem.EncodedCatchHandler, CodeItem.EncodedCatchHandler>();
+
+ TryRange tryRange = firstTryRange.next;
+
+ while (tryRange != lastTryRange) {
+ CodeItem.EncodedTypeAddrPair[] encodedTypeAddrPairs =
+ new CodeItem.EncodedTypeAddrPair[tryRange.handlers.size()];
+
+ int index = 0;
+ for (Handler handler: tryRange.handlers) {
+ CodeItem.EncodedTypeAddrPair encodedTypeAddrPair = new CodeItem.EncodedTypeAddrPair(
+ handler.type,
+ handler.handlerAddress);
+ encodedTypeAddrPairs[index++] = encodedTypeAddrPair;
+ }
+
+ CodeItem.EncodedCatchHandler encodedCatchHandler = new CodeItem.EncodedCatchHandler(
+ encodedTypeAddrPairs,
+ tryRange.catchAllHandlerAddress);
+ CodeItem.EncodedCatchHandler internedEncodedCatchHandler = handlerDict.get(encodedCatchHandler);
+ if (internedEncodedCatchHandler == null) {
+ handlerDict.put(encodedCatchHandler, encodedCatchHandler);
+ handlers.add(encodedCatchHandler);
+ } else {
+ encodedCatchHandler = internedEncodedCatchHandler;
+ }
+
+ CodeItem.TryItem tryItem = new CodeItem.TryItem(
+ tryRange.startAddress,
+ tryRange.endAddress - tryRange.startAddress,
+ encodedCatchHandler);
+ tries.add(tryItem);
+
+ tryRange = tryRange.next;
+ }
+
+ return new Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>>(tries, handlers);
+ }
+
+ public void addCatchAllHandler(int startAddress, int endAddress, int handlerAddress) {
+ TryRange startRange;
+ TryRange endRange;
+
+ Pair<TryRange, TryRange> ranges = getBoundingRanges(startAddress, endAddress);
+ startRange = ranges.first;
+ endRange = ranges.second;
+
+ int previousEnd = startAddress;
+ TryRange tryRange = startRange;
+
+ /*Now we have the start and end ranges that exactly match the start and end
+ of the range being added. We need to iterate over all the ranges from the start
+ to end range inclusively, and append the handler to the end of each range's handler
+ list. We also need to create a new range for any "holes" in the existing ranges*/
+ do
+ {
+ //is there a hole? If so, add a new range to fill the hole
+ if (tryRange.startAddress > previousEnd) {
+ TryRange newRange = new TryRange(previousEnd, tryRange.startAddress);
+ tryRange.prepend(newRange);
+ tryRange = newRange;
+ }
+
+ if (tryRange.catchAllHandlerAddress == -1) {
+ tryRange.catchAllHandlerAddress = handlerAddress;
+ }
+
+ previousEnd = tryRange.endAddress;
+ tryRange = tryRange.next;
+ } while (tryRange.previous != endRange);
+ }
+
+ public Pair<TryRange, TryRange> getBoundingRanges(int startAddress, int endAddress) {
+ TryRange startRange = null;
+ TryRange endRange = null;
+
+ TryRange tryRange = firstTryRange.next;
+ while (tryRange != lastTryRange) {
+ if (startAddress == tryRange.startAddress) {
+ //|-----|
+ //^------
+ /*Bam. We hit the start of the range right on the head*/
+ startRange = tryRange;
+ break;
+ } else if (startAddress > tryRange.startAddress && startAddress < tryRange.endAddress) {
+ //|-----|
+ // ^----
+ /*Almost. The start of the range being added is in the middle
+ of an existing try range. We need to split the existing range
+ at the start address of the range being added*/
+ startRange = tryRange.split(startAddress);
+ break;
+ }else if (startAddress < tryRange.startAddress) {
+ if (endAddress <= tryRange.startAddress) {
+ // |-----|
+ //^--^
+ /*Oops, totally too far! The new range doesn't overlap any existing
+ ones, so we just add it and return*/
+ startRange = new TryRange(startAddress, endAddress);
+ tryRange.prepend(startRange);
+ return new Pair<TryRange, TryRange>(startRange, startRange);
+ } else {
+ // |-----|
+ //^---------
+ /*Oops, too far! We've passed the start of the range being added, but
+ the new range does overlap this one. We need to add a new range just
+ before this one*/
+ startRange = new TryRange(startAddress, tryRange.startAddress);
+ tryRange.prepend(startRange);
+ break;
+ }
+ }
+
+ tryRange = tryRange.next;
+ }
+
+ //|-----|
+ // ^-----
+ /*Either the list of tries is blank, or all the tries in the list
+ end before the range being added starts. In either case, we just need
+ to add a new range at the end of the list*/
+ if (startRange == null) {
+ startRange = new TryRange(startAddress, endAddress);
+ lastTryRange.prepend(startRange);
+ return new Pair<TryRange, TryRange>(startRange, startRange);
+ }
+
+ tryRange = startRange;
+ while (tryRange != lastTryRange) {
+ if (tryRange.endAddress == endAddress) {
+ //|-----|
+ //------^
+ /*Bam! We hit the end right on the head.*/
+ endRange = tryRange;
+ break;
+ } else if (tryRange.startAddress < endAddress && tryRange.endAddress > endAddress) {
+ //|-----|
+ //--^
+ /*Almost. The range being added ends in the middle of an
+ existing range. We need to split the existing range
+ at the end of the range being added.*/
+ tryRange.split(endAddress);
+ endRange = tryRange;
+ break;
+ } else if (tryRange.startAddress >= endAddress) {
+ //|-----| |-----|
+ //-----------^
+ /*Oops, too far! The current range starts after the range being added
+ ends. We need to create a new range that starts at the end of the
+ previous range, and ends at the end of the range being added*/
+ endRange = new TryRange(tryRange.previous.endAddress, endAddress);
+ tryRange.prepend(endRange);
+ break;
+ }
+ tryRange = tryRange.next;
+ }
+
+ //|-----|
+ //--------^
+ /*The last range in the list ended before the end of the range being added.
+ We need to add a new range that starts at the end of the last range in the
+ list, and ends at the end of the range being added.*/
+ if (endRange == null) {
+ endRange = new TryRange(lastTryRange.previous.endAddress, endAddress);
+ lastTryRange.prepend(endRange);
+ }
+
+ return new Pair<TryRange, TryRange>(startRange, endRange);
+ }
+
+ public void addHandler(TypeIdItem type, int startAddress, int endAddress, int handlerAddress) {
+ TryRange startRange;
+ TryRange endRange;
+
+ //TODO: need to check for pre-existing exception types in the handler list?
+
+ Pair<TryRange, TryRange> ranges = getBoundingRanges(startAddress, endAddress);
+ startRange = ranges.first;
+ endRange = ranges.second;
+ Handler handler = new Handler(type, handlerAddress);
+
+ int previousEnd = startAddress;
+ TryRange tryRange = startRange;
+
+ /*Now we have the start and end ranges that exactly match the start and end
+ of the range being added. We need to iterate over all the ranges from the start
+ to end range inclusively, and append the handler to the end of each range's handler
+ list. We also need to create a new range for any "holes" in the existing ranges*/
+ do
+ {
+ //is there a hole? If so, add a new range to fill the hole
+ if (tryRange.startAddress > previousEnd) {
+ TryRange newRange = new TryRange(previousEnd, tryRange.startAddress);
+ tryRange.prepend(newRange);
+ tryRange = newRange;
+ }
+
+ tryRange.appendHandler(handler);
+ previousEnd = tryRange.endAddress;
+ tryRange = tryRange.next;
+ } while (tryRange.previous != endRange);
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java b/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java
new file mode 100644
index 0000000..d064a4d
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.*;
+
+/**
+ * Class that takes a combined output destination and provides two
+ * output writers, one of which ends up writing to the left column and
+ * one which goes on the right.
+ */
+public final class TwoColumnOutput {
+ /** non-null; underlying writer for final output */
+ private final Writer out;
+
+ /** &gt; 0; the left column width */
+ private final int leftWidth;
+
+ /** non-null; pending left column output */
+ private final StringBuffer leftBuf;
+
+ /** non-null; pending right column output */
+ private final StringBuffer rightBuf;
+
+ /** non-null; left column writer */
+ private final IndentingWriter leftColumn;
+
+ /** non-null; right column writer */
+ private final IndentingWriter rightColumn;
+
+ /**
+ * Turns the given two strings (with widths) and spacer into a formatted
+ * two-column string.
+ *
+ * @param s1 non-null; first string
+ * @param width1 &gt; 0; width of the first column
+ * @param spacer non-null; spacer string
+ * @param s2 non-null; second string
+ * @param width2 &gt; 0; width of the second column
+ * @return non-null; an appropriately-formatted string
+ */
+ public static String toString(String s1, int width1, String spacer,
+ String s2, int width2) {
+ int len1 = s1.length();
+ int len2 = s2.length();
+
+ StringWriter sw = new StringWriter((len1 + len2) * 3);
+ TwoColumnOutput twoOut =
+ new TwoColumnOutput(sw, width1, width2, spacer);
+
+ try {
+ twoOut.getLeft().write(s1);
+ twoOut.getRight().write(s2);
+ } catch (IOException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ twoOut.flush();
+ return sw.toString();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; writer to send final output to
+ * @param leftWidth &gt; 0; width of the left column, in characters
+ * @param rightWidth &gt; 0; width of the right column, in characters
+ * @param spacer non-null; spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
+ String spacer) {
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (leftWidth < 1) {
+ throw new IllegalArgumentException("leftWidth < 1");
+ }
+
+ if (rightWidth < 1) {
+ throw new IllegalArgumentException("rightWidth < 1");
+ }
+
+ if (spacer == null) {
+ throw new NullPointerException("spacer == null");
+ }
+
+ StringWriter leftWriter = new StringWriter(1000);
+ StringWriter rightWriter = new StringWriter(1000);
+
+ this.out = out;
+ this.leftWidth = leftWidth;
+ this.leftBuf = leftWriter.getBuffer();
+ this.rightBuf = rightWriter.getBuffer();
+ this.leftColumn = new IndentingWriter(leftWriter, leftWidth);
+ this.rightColumn =
+ new IndentingWriter(rightWriter, rightWidth, spacer);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out non-null; stream to send final output to
+ * @param leftWidth &gt;= 1; width of the left column, in characters
+ * @param rightWidth &gt;= 1; width of the right column, in characters
+ * @param spacer non-null; spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth,
+ String spacer) {
+ this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
+ }
+
+ /**
+ * Gets the writer to use to write to the left column.
+ *
+ * @return non-null; the left column writer
+ */
+ public Writer getLeft() {
+ return leftColumn;
+ }
+
+ /**
+ * Gets the writer to use to write to the right column.
+ *
+ * @return non-null; the right column writer
+ */
+ public Writer getRight() {
+ return rightColumn;
+ }
+
+ /**
+ * Flushes the output. If there are more lines of pending output in one
+ * column, then the other column will get filled with blank lines.
+ */
+ public void flush() {
+ try {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+ outputFullLines();
+ flushLeft();
+ flushRight();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Outputs to the final destination as many full line pairs as
+ * there are in the pending output, removing those lines from
+ * their respective buffers. This method terminates when at
+ * least one of the two column buffers is empty.
+ */
+ private void outputFullLines() throws IOException {
+ for (;;) {
+ int leftLen = leftBuf.indexOf("\n");
+ if (leftLen < 0) {
+ return;
+ }
+
+ int rightLen = rightBuf.indexOf("\n");
+ if (rightLen < 0) {
+ return;
+ }
+
+ if (leftLen != 0) {
+ out.write(leftBuf.substring(0, leftLen));
+ }
+
+ if (rightLen != 0) {
+ writeSpaces(out, leftWidth - leftLen);
+ out.write(rightBuf.substring(0, rightLen));
+ }
+
+ out.write('\n');
+
+ leftBuf.delete(0, leftLen + 1);
+ rightBuf.delete(0, rightLen + 1);
+ }
+ }
+
+ /**
+ * Flushes the left column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushLeft() throws IOException {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+
+ while (leftBuf.length() != 0) {
+ rightColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Flushes the right column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushRight() throws IOException {
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+
+ while (rightBuf.length() != 0) {
+ leftColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Appends a newline to the given buffer via the given writer, but
+ * only if it isn't empty and doesn't already end with one.
+ *
+ * @param buf non-null; the buffer in question
+ * @param out non-null; the writer to use
+ */
+ private static void appendNewlineIfNecessary(StringBuffer buf,
+ Writer out)
+ throws IOException {
+ int len = buf.length();
+
+ if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
+ out.write('\n');
+ }
+ }
+
+ /**
+ * Writes the given number of spaces to the given writer.
+ *
+ * @param out non-null; where to write
+ * @param amt &gt;= 0; the number of spaces to write
+ */
+ private static void writeSpaces(Writer out, int amt) throws IOException {
+ while (amt > 0) {
+ out.write(' ');
+ amt--;
+ }
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java b/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java
new file mode 100644
index 0000000..40cedda
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java
@@ -0,0 +1,64 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver (JesusFreke)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib.Util;
+
+import org.jf.dexlib.EncodedValue.*;
+import org.jf.dexlib.TypeIdItem;
+
+public class TypeUtils
+{
+ public static EncodedValue makeDefaultValueForType(String type) {
+ switch (type.charAt(0)) {
+ case 'Z':
+ return BooleanEncodedValue.FalseValue;
+ case 'B':
+ return new ByteEncodedValue((byte)0);
+ case 'S':
+ return new ShortEncodedValue((short)0);
+ case 'C':
+ return new CharEncodedValue((char)0);
+ case 'I':
+ return new IntEncodedValue(0);
+ case 'J':
+ return new LongEncodedValue(0);
+ case 'F':
+ return new FloatEncodedValue(0);
+ case 'D':
+ return new DoubleEncodedValue(0);
+ case 'L':
+ case '[':
+ return NullEncodedValue.NullValue;
+ }
+ return null;
+ }
+
+ public static EncodedValue makeDefaultValueForType(TypeIdItem type) {
+ return makeDefaultValueForType(type.getTypeDescriptor());
+ }
+}
diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java b/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java
new file mode 100644
index 0000000..0011bc5
--- /dev/null
+++ b/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * As per the Apache license requirements, this file has been modified
+ * from its original state.
+ *
+ * Such modifications are Copyright (C) 2010 Ben Gruver, and are released
+ * under the original license
+ */
+
+package org.jf.dexlib.Util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Constants of type <code>CONSTANT_Utf8_info</code>.
+ */
+public final class Utf8Utils {
+
+
+ /**
+ * Converts a string into its Java-style UTF-8 form. Java-style UTF-8
+ * differs from normal UTF-8 in the handling of character '\0' and
+ * surrogate pairs.
+ *
+ * @param string non-null; the string to convert
+ * @return non-null; the UTF-8 bytes for it
+ */
+ public static byte[] stringToUtf8Bytes(String string) {
+ int len = string.length();
+ byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
+ int outAt = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = string.charAt(i);
+ if ((c != 0) && (c < 0x80)) {
+ bytes[outAt] = (byte) c;
+ outAt++;
+ } else if (c < 0x800) {
+ bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
+ bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 2;
+ } else {
+ bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
+ bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
+ bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 3;
+ }
+ }
+
+ byte[] result = new byte[outAt];
+ System.arraycopy(bytes, 0, result, 0, outAt);
+ return result;
+ }
+
+ private static char[] tempBuffer = null;
+
+ /**
+ * Converts an array of UTF-8 bytes into a string.
+ *
+ * This method uses a global buffer to avoid having to allocate one every time, so it is *not* thread-safe
+ *
+ * @param bytes non-null; the bytes to convert
+ * @param start the start index of the utf8 string to convert
+ * @param length the length of the utf8 string to convert, not including any null-terminator that might be present
+ * @return non-null; the converted string
+ */
+ public static String utf8BytesToString(byte[] bytes, int start, int length) {
+ if (tempBuffer == null || tempBuffer.length < length) {
+ tempBuffer = new char[length];
+ }
+ char[] chars = tempBuffer;
+ int outAt = 0;
+
+ for (int at = start; length > 0; /*at*/) {
+ int v0 = bytes[at] & 0xFF;
+ char out;
+ switch (v0 >> 4) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07: {
+ // 0XXXXXXX -- single-byte encoding
+ length--;
+ if (v0 == 0) {
+ // A single zero byte is illegal.
+ return throwBadUtf8(v0, at);
+ }
+ out = (char) v0;
+ at++;
+ break;
+ }
+ case 0x0c: case 0x0d: {
+ // 110XXXXX -- two-byte encoding
+ length -= 2;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes[at + 1] & 0xFF;
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ /*
+ * This should have been represented with
+ * one-byte encoding.
+ */
+ return throwBadUtf8(v1, at + 1);
+ }
+ out = (char) value;
+ at += 2;
+ break;
+ }
+ case 0x0e: {
+ // 1110XXXX -- three-byte encoding
+ length -= 3;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes[at + 1] & 0xFF;
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int v2 = bytes[at + 2] & 0xFF;
+ if ((v2 & 0xc0) != 0x80) {
+ return throwBadUtf8(v2, at + 2);
+ }
+ int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
+ (v2 & 0x3f);
+ if (value < 0x800) {
+ /*
+ * This should have been represented with one- or
+ * two-byte encoding.
+ */
+ return throwBadUtf8(v2, at + 2);
+ }
+ out = (char) value;
+ at += 3;
+ break;
+ }
+ default: {
+ // 10XXXXXX, 1111XXXX -- illegal
+ return throwBadUtf8(v0, at);
+ }
+ }
+ chars[outAt] = out;
+ outAt++;
+ }
+
+ return new String(chars, 0, outAt);
+ }
+
+ /**
+ * Helper for {@link #utf8BytesToString}, which throws the right
+ * exception for a bogus utf-8 byte.
+ *
+ * @param value the byte value
+ * @param offset the file offset
+ * @return never
+ * @throws IllegalArgumentException always thrown
+ */
+ private static String throwBadUtf8(int value, int offset) {
+ throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
+ " at offset " + Hex.u4(offset));
+ }
+
+ public static void writeEscapedChar(Writer writer, char c) throws IOException {
+ if ((c >= ' ') && (c < 0x7f)) {
+ if ((c == '\'') || (c == '\"') || (c == '\\')) {
+ writer.write('\\');
+ }
+ writer.write(c);
+ return;
+ } else if (c <= 0x7f) {
+ switch (c) {
+ case '\n': writer.write("\\n"); return;
+ case '\r': writer.write("\\r"); return;
+ case '\t': writer.write("\\t"); return;
+ }
+ }
+
+ writer.write("\\u");
+ writer.write(Character.forDigit(c >> 12, 16));
+ writer.write(Character.forDigit((c >> 8) & 0x0f, 16));
+ writer.write(Character.forDigit((c >> 4) & 0x0f, 16));
+ writer.write(Character.forDigit(c & 0x0f, 16));
+
+ }
+
+ public static void writeEscapedString(Writer writer, String value) throws IOException {
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+
+ if ((c >= ' ') && (c < 0x7f)) {
+ if ((c == '\'') || (c == '\"') || (c == '\\')) {
+ writer.write('\\');
+ }
+ writer.write(c);
+ continue;
+ } else if (c <= 0x7f) {
+ switch (c) {
+ case '\n': writer.write("\\n"); continue;
+ case '\r': writer.write("\\r"); continue;
+ case '\t': writer.write("\\t"); continue;
+ }
+ }
+
+ writer.write("\\u");
+ writer.write(Character.forDigit(c >> 12, 16));
+ writer.write(Character.forDigit((c >> 8) & 0x0f, 16));
+ writer.write(Character.forDigit((c >> 4) & 0x0f, 16));
+ writer.write(Character.forDigit(c & 0x0f, 16));
+ }
+ }
+
+ public static String escapeString(String value) {
+ int len = value.length();
+ StringBuilder sb = new StringBuilder(len * 3 / 2);
+
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+
+ if ((c >= ' ') && (c < 0x7f)) {
+ if ((c == '\'') || (c == '\"') || (c == '\\')) {
+ sb.append('\\');
+ }
+ sb.append(c);
+ continue;
+ } else if (c <= 0x7f) {
+ switch (c) {
+ case '\n': sb.append("\\n"); continue;
+ case '\r': sb.append("\\r"); continue;
+ case '\t': sb.append("\\t"); continue;
+ }
+ }
+
+ sb.append("\\u");
+ sb.append(Character.forDigit(c >> 12, 16));
+ sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
+ sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
+ sb.append(Character.forDigit(c & 0x0f, 16));
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/dexlib/version b/dexlib/version
new file mode 100644
index 0000000..9df886c
--- /dev/null
+++ b/dexlib/version
@@ -0,0 +1 @@
+1.4.2